This commit is contained in:
nlohmann committed 2026-10-27 12:14:00 +00:00
1 parent b36f8a9821
commit 11a863958e
1180 files changed
+14211 -6025

No files matched your search

+19 -7
View File
@@ -1,4 +1,4 @@
<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=author content="Niels Lohmann"><link href="https://json.nlohmann.me/features/json_view/" rel="canonical"><link href=../types/template_parameters/ rel=prev><link href=../../integration/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.7"><title>Zero-copy JSON views - JSON for Modern C++</title><link rel=stylesheet href=../../assets/stylesheets/main.ec1eaa64.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel="stylesheet" href="../../assets/external/fonts.googleapis.com/css.61a430c9.css"><style>:root{--md-text-font:"Roboto";--md-code-font:"JetBrains Mono"}</style><link rel=stylesheet href=../../css/custom.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=indigo data-md-color-accent=indigo> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#zero-copy-json-views class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../.. title="JSON for Modern C++" class="md-header__button md-logo" aria-label="JSON for Modern C++" data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> JSON for Modern C++ </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Zero-copy JSON views </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme=default data-md-color-primary=indigo data-md-color-accent=indigo aria-label="Switch to dark mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme=slate data-md-color-primary=indigo data-md-color-accent=indigo aria-label="Switch to light mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 Line truncated
<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="JSON for Modern C++ is a header-only C++11 library that makes JSON a first-class data type."><meta name=author content="Niels Lohmann"><link href="https://json.nlohmann.me/features/json_view/" rel="canonical"><link href=../types/template_parameters/ rel=prev><link href=../../integration/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.7"><title>Zero-copy JSON views - JSON for Modern C++</title><link rel=stylesheet href=../../assets/stylesheets/main.ec1eaa64.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=stylesheet href=../../css/custom.css><link rel=stylesheet href=../../css/brand.css><link rel=stylesheet href=../../css/fig-sax.css><link rel=stylesheet href=../../css/fig-jsonview.css><link rel=stylesheet href=../../css/fig-exceptions.css><link rel=stylesheet href=../../css/fig-pointer.css><link rel=stylesheet href=../../css/fig-charts.css><link rel=stylesheet href=../../css/fig-integration.css><link rel=stylesheet href=../../css/fig-macros.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#zero-copy-json-views class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../.. title="JSON for Modern C++" class="md-header__button md-logo" aria-label="JSON for Modern C++" data-md-component=logo> <img src=../../images/wordmark-header.svg alt=logo> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> JSON for Modern C++ </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Zero-copy JSON views </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to dark mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme=slate data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to light mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9Line truncated
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;nlohmann/json_view.hpp&gt;</span>
<span class=k>using</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json_document</span><span class=p>;</span>
@@ -16,7 +16,7 @@
<span class=p>}</span>
</code></pre></div> <p>Output:</p> <div class=highlight><pre><span></span><code><span class=kc>true</span>
<span class=p>{</span><span class=nt>"numbers"</span><span class=p>:[</span><span class=mi>1</span><span class=p>,</span><span class=mi>2</span><span class=p>,</span><span class=mi>3</span><span class=p>],</span><span class=nt>"pi"</span><span class=p>:</span><span class=mf>3.14</span><span class=p>}</span>
</code></pre></div> </details> <p><a href=../../api/ordered_json_document/ ><code>ordered_json_document</code></a> is the equivalent for <a href=../../api/ordered_json/ ><code>ordered_json</code></a>, just as <a href=../../api/ordered_json/ ><code>ordered_json</code></a> is to <a href=../../api/json/ ><code>json</code></a>.</p> <h2 id=ownership-and-lifetime>Ownership and lifetime<a class=headerlink href=#ownership-and-lifetime title="Permanent link">&para;</a></h2> <p>A document either <strong>borrows</strong> the text it was parsed from, or <strong>owns</strong> its own copy of it; call <a href=../../api/basic_json_document/owns_source/ ><code>owns_source()</code></a> to find out which happened. <a href=../../api/basic_json_document/parse/ ><code>parse()</code></a> decides this from the value category and type of its argument (an lvalue <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code> is borrowed; an rvalue <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code> is moved in, owned without a copy; a stream is read into an owned buffer; and so on -- see <a href=../../api/basic_json_document/parse/#notes><code>parse</code>'s Notes</a> for the full table). <a href=../../api/basic_json_document/parse_copy/ ><code>parse_copy()</code></a> always owns a copy, regardless of the input.</p> <div class="admonition warning"> <p class=admonition-title>A borrowed document depends on your buffer</p> <p>If a document borrows its text, that text <strong>must outlive the document</strong> (and every view taken from it) and must stay <strong>unmodified</strong> for as long as the document exists, since floating-point values and unescaped strings are read from it lazily. Reading or writing through a view after the underlying buffer is gone is undefined behavior, exactly as it would be for a dangling <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string_view</span></code>.</p> </div> <p>A view is valid only while all of the following hold:</p> <ul> <li>the document is alive,</li> <li>the document has not been re-parsed since the view was taken (with <a href=../../api/basic_json_document/read/ ><code>read()</code></a> or <a href=../../api/basic_json_document/parse/ ><code>parse()</code></a> into it),</li> <li>the document has not had <a href=../../api/basic_json_document/shrink_to_fit/ ><code>shrink_to_fit()</code></a> called on it since, and</li> <li>if the document borrows its source text, that text is still alive and unmodified.</li> </ul> <p>Moving the <code>basic_json_document&lt;&gt;</code> itself is fine and does <strong>not</strong> invalidate its views: the index is a separate heap allocation that keeps its address across the move. Take a fresh view from <a href=../../api/basic_json_document/root/ ><code>root()</code></a> whenever any of the other conditions above was not met.</p> <p>A view can dangle, exactly like a <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string_view</span></code>: nothing stops you from using it after its document is gone. This is deliberate. A view is two pointers and trivially copyable; making it safe (a <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>shared_ptr</span></code> in the document and a <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>weak_ptr</span></code> in each view) would make it larger, add reference counting to every copy, and a <code>lock()</code> to every access -- exactly the costs the view exists to avoid. What is caught is the obvious case: because a view dies with its document, <a href=../../api/basic_json_document/root/ ><code>root()</code></a> is not callable on a temporary document: <code class=highlight><span class=k>auto</span><span class=w> </span><span class=n>v</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json_document</span><span class=o>::</span><span class=n>parse</span><span class=p>(</span><span class=n>text</span><span class=p>).</span><span class=n>root</span><span class=p>();</span></code> does not compile. Give the document a name first.</p> <details class=example> <summary>Example: borrowed and owned documents, and when views become invalid</summary> <div class=highlight><pre><span></span><code><span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;iostream&gt;</span>
</code></pre></div> </details> <p><a href=../../api/ordered_json_document/ ><code>ordered_json_document</code></a> is the equivalent for <a href=../../api/ordered_json/ ><code>ordered_json</code></a>, just as <a href=../../api/ordered_json/ ><code>ordered_json</code></a> is to <a href=../../api/json/ ><code>json</code></a>.</p> <h2 id=ownership-and-lifetime>Ownership and lifetime<a class=headerlink href=#ownership-and-lifetime title="Permanent link">&para;</a></h2> <p>A document either <strong>borrows</strong> the text it was parsed from, or <strong>owns</strong> its own copy of it; call <a href=../../api/basic_json_document/owns_source/ ><code>owns_source()</code></a> to find out which happened. <a href=../../api/basic_json_document/parse/ ><code>parse()</code></a> decides this from the value category and type of its argument (an lvalue <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code> is borrowed; an rvalue <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code> is moved in, owned without a copy; a stream is read into an owned buffer; and so on — see <a href=../../api/basic_json_document/parse/#notes><code>parse</code>'s Notes</a> for the full table). <a href=../../api/basic_json_document/parse_copy/ ><code>parse_copy()</code></a> always owns a copy, regardless of the input.</p> <div class="admonition warning"> <p class=admonition-title>A borrowed document depends on your buffer</p> <p>If a document borrows its text, that text <strong>must outlive the document</strong> (and every view taken from it) and must stay <strong>unmodified</strong> for as long as the document exists, since floating-point values and unescaped strings are read from it lazily. Reading or writing through a view after the underlying buffer is gone is undefined behavior, exactly as it would be for a dangling <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string_view</span></code>.</p> </div> <p>A view is valid only while all of the following hold:</p> <ul> <li>the document is alive,</li> <li>the document has not been re-parsed since the view was taken (with <a href=../../api/basic_json_document/read/ ><code>read()</code></a> or <a href=../../api/basic_json_document/parse/ ><code>parse()</code></a> into it),</li> <li>the document has not had <a href=../../api/basic_json_document/shrink_to_fit/ ><code>shrink_to_fit()</code></a> called on it since, and</li> <li>if the document borrows its source text, that text is still alive and unmodified.</li> </ul> <p>Moving the <code>basic_json_document&lt;&gt;</code> itself is fine and does <strong>not</strong> invalidate its views: the index is a separate heap allocation that keeps its address across the move. Take a fresh view from <a href=../../api/basic_json_document/root/ ><code>root()</code></a> whenever any of the other conditions above was not met.</p> <div class=nj-fig-wrap> <p><svg class="nj-fig nj-jsonview " viewbox="0 0 840 572" role=img aria-labelledby="fig-jvt-title fig-jvt-title-d" xmlns=http://www.w3.org/2000/svg> <title id=fig-jvt-title>When a json_view becomes invalid</title> <desc id=fig-jvt-title-d>Four timelines. First: a document borrows a std::string; when the string is destroyed, the document and every view taken from it dangle. Second: doc.read() re-parses the document; views taken before are invalid, a fresh view from root() is valid. Third: doc.shrink_to_fit() has the same effect. Fourth: moving the document does not invalidate its views; destroying the moved-to document does.</desc> <defs> <marker id=jvt-bad viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-bad nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> </defs> <rect x=210 y=12 width=34 height=12 rx=3 class="nj-jsonview-bar nj-jsonview-c-struct"/> <text class=nj-jsonview-note x=252 y=23 xml:space=preserve>alive / valid</text> <rect class=nj-jsonview-bad x=370 y=12 width=34 height=12 rx=3 /> <text class=nj-jsonview-note x=412 y=23 xml:space=preserve>dangling: using it is undefined behavior</text> <path class="nj-jsonview-arr nj-jsonview-c-line" d="M210 44 L820 44"/> <text class=nj-jsonview-tiny x=820 y=38 text-anchor=end xml:space=preserve>time →</text> <text class="nj-jsonview-h nj-jsonview-sm2" x=16 y=74 xml:space=preserve>The borrowed text is destroyed</text> <text class="nj-jsonview-mono nj-jsonview-sm" x=198 y=99 text-anchor=end xml:space=preserve>std::string text</text> <rect x=210 y=87 width=260 height=16 rx=3 class="nj-jsonview-bar nj-jsonview-c-struct"/> <text class="nj-jsonview-mono nj-jsonview-sm" x=198 y=127 text-anchor=end xml:space=preserve>json_document doc</text> <rect x=270 y=115 width=200 height=16 rx=3 class="nj-jsonview-bar nj-jsonview-c-struct"/> <rect class=nj-jsonview-bad x=470 y=115 width=350 height=16 rx=3 /> <text class="nj-jsonview-mono nj-jsonview-sm" x=198 y=155 text-anchor=endLine truncated
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;string&gt;</span>
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;utility&gt;</span>
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;nlohmann/json_view.hpp&gt;</span>
@@ -53,7 +53,7 @@
<span class=kc>true</span>
<span class=kc>true</span>
<span class=kc>true</span>
</code></pre></div> </details> <h2 id=what-is-the-same-as-parse>What is the same as <code>parse()</code><a class=headerlink href=#what-is-the-same-as-parse title="Permanent link">&para;</a></h2> <ul> <li><strong>Accept/reject.</strong> <a href=../../api/basic_json_document/accept/ ><code>accept()</code></a> and <a href=../../api/basic_json_document/parse/ ><code>parse()</code></a> accept and reject exactly the same inputs as <a href=../../api/basic_json/accept/ ><code>basic_json::accept()</code></a>/<a href=../../api/basic_json/parse/ ><code>basic_json::parse()</code></a>, with the same <code>ignore_comments</code> and <code>ignore_trailing_commas</code> options.</li> <li><strong>Errors.</strong> A failing parse throws the same exception -- the same id, message, and position -- because on failure the library's own parser is run on the same bytes to produce the diagnostic. Valid input never pays for this, and the replay builds no values: it costs about a lexing pass up to the error. <code class=highlight><span class=n>allow_exceptions</span><span class=w> </span><span class=o>==</span><span class=w> </span><span class=nb>false</span></code> skips the replay and gives a <a href=../../api/basic_json_document/is_discarded/ >discarded</a> document instead of throwing, just as it gives a discarded value for <code class=highlight><span class=n>basic_json</span><span class=o>::</span><span class=n>parse</span><span class=p>()</span></code>.</li> <li><strong>Number classification.</strong> An integer literal that does not fit into the 64-bit integer type becomes a floating-point value, exactly as it does for <code class=highlight><span class=n>basic_json</span><span class=o>::</span><span class=n>parse</span><span class=p>()</span></code>.</li> <li><strong>Macros.</strong> <a href=../../api/macros/json_strict_nul_handling/ ><code>JSON_STRICT_NUL_HANDLING</code></a> and <a href=../../api/macros/json_noexception/ ><code>JSON_NOEXCEPTION</code></a>/<a href=../../api/macros/json_throw_user/ ><code>JSON_THROW_USER</code></a> behave the same way they do for <code>&lt;nlohmann/json.hpp&gt;</code>. These are the macros that change what the parser accepts or how it reports errors. The others either do not apply (<a href=../../api/macros/json_diagnostics/ ><code>JSON_DIAGNOSTICS</code></a> needs parent pointers in <code>basic_json</code> values) or only affect the values <code>materialize()</code> produces, through <code>BasicJsonType</code>.</li> </ul> <h2 id=what-is-different>What is different<a class=headerlink href=#what-is-different title="Permanent link">&para;</a></h2> <ul> <li><strong>Only 64-bit integers.</strong> <code>basic_json_document&lt;BasicJsonType&gt;</code> requires <code>BasicJsonType::number_integer_t</code> and <code>number_unsigned_t</code> to both be 64 bits wide; this is a compile-time <code class=highlight><span class=k>static_assert</span></code>.</li> <li><strong>A 4 <abbr title="Gibibyte (2^30 bytes)">GiB</abbr> input limit.</strong> An input of 4294967280 bytes (4 <abbr title="Gibibyte (2^30 bytes)">GiB</abbr> minus 16 bytes) or more throws <a href=../../home/exceptions/#jsonexceptionout_of_range416><code>out_of_range.416</code></a>, a limit <code class=highlight><span class=n>basic_json</span><span class=o>::</span><span class=n>parse</span><span class=p>()</span></code> does not have. It keeps an index entry at 16 bytes: offsets and counts are 32 bits wide. The limit is 16 bytes below 2^32, because the parser reads in 16-byte steps, and a position one step past the end must still fit.</li> <li><strong>A stream is always read to its end.</strong> There is no partial/streaming read of an <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>istream</span></code>: the stream is read to its end into a buffer the document owns, and parsing starts after that.</li> <li><strong>No source positions on <code>materialize()</code>.</strong> Even with <a href=../../api/macros/json_diagnostic_positions/ ><code>JSON_DIAGNOSTIC_POSITIONS</code></a> enabled, <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a> does not set them. The index has the start offset of every value, but not the end of arrays, objects, and escaped strings, which the positions also need.</li> <li><strong>Objects iterate in document order.</strong> <a href=../../api/basic_json_view/begin/ ><code>begin()</code></a>/ <a href=../../api/basic_json_view/end/ ><code>end()</code></a> and <a href=../../api/basic_json_view/items/ ><code>items()</code></a> visit an object's members in the order they appear in the source text. <code>basic_json</code>'s default <code>object_t</code> is a <code>std::map</code>, which sorts by key, so iterating a <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a>d value can print members in a different order than iterating the view they came from.</li> <li><strong>Chained access is safe.</strong> <a href=../../api/basic_json_view/operatLine truncated
</code></pre></div> </details> <h2 id=what-is-the-same-as-parse>What is the same as <code>parse()</code><a class=headerlink href=#what-is-the-same-as-parse title="Permanent link">&para;</a></h2> <ul> <li><strong>Accept/reject.</strong> <a href=../../api/basic_json_document/accept/ ><code>accept()</code></a> and <a href=../../api/basic_json_document/parse/ ><code>parse()</code></a> accept and reject exactly the same inputs as <a href=../../api/basic_json/accept/ ><code>basic_json::accept()</code></a>/<a href=../../api/basic_json/parse/ ><code>basic_json::parse()</code></a>, with the same <code>ignore_comments</code> and <code>ignore_trailing_commas</code> options.</li> <li><strong>Errors.</strong> A failing parse throws the same exception — the same id, message, and position — because on failure the library's own parser is run on the same bytes to produce the diagnostic. Valid input never pays for this, and the replay builds no values: it costs about a lexing pass up to the error. <code class=highlight><span class=n>allow_exceptions</span><span class=w> </span><span class=o>==</span><span class=w> </span><span class=nb>false</span></code> skips the replay and gives a <a href=../../api/basic_json_document/is_discarded/ >discarded</a> document instead of throwing, just as it gives a discarded value for <code class=highlight><span class=n>basic_json</span><span class=o>::</span><span class=n>parse</span><span class=p>()</span></code>.</li> <li><strong>Number classification.</strong> An integer literal that does not fit into the 64-bit integer type becomes a floating-point value, exactly as it does for <code class=highlight><span class=n>basic_json</span><span class=o>::</span><span class=n>parse</span><span class=p>()</span></code>.</li> <li><strong>Macros.</strong> <a href=../../api/macros/json_strict_nul_handling/ ><code>JSON_STRICT_NUL_HANDLING</code></a> and <a href=../../api/macros/json_noexception/ ><code>JSON_NOEXCEPTION</code></a>/<a href=../../api/macros/json_throw_user/ ><code>JSON_THROW_USER</code></a> behave the same way they do for <code>&lt;nlohmann/json.hpp&gt;</code>. These are the macros that change what the parser accepts or how it reports errors. The others either do not apply (<a href=../../api/macros/json_diagnostics/ ><code>JSON_DIAGNOSTICS</code></a> needs parent pointers in <code>basic_json</code> values) or only affect the values <code>materialize()</code> produces, through <code>BasicJsonType</code>.</li> </ul> <h2 id=what-is-different>What is different<a class=headerlink href=#what-is-different title="Permanent link">&para;</a></h2> <ul> <li><strong>Only 64-bit integers.</strong> <code>basic_json_document&lt;BasicJsonType&gt;</code> requires <code>BasicJsonType::number_integer_t</code> and <code>number_unsigned_t</code> to both be 64 bits wide; this is a compile-time <code class=highlight><span class=k>static_assert</span></code>.</li> <li><strong>A 4 <abbr title="Gibibyte (2^30 bytes)">GiB</abbr> input limit.</strong> An input of 4294967280 bytes (4 <abbr title="Gibibyte (2^30 bytes)">GiB</abbr> minus 16 bytes) or more throws <a href=../../home/exceptions/#jsonexceptionout_of_range416><code>out_of_range.416</code></a>, a limit <code class=highlight><span class=n>basic_json</span><span class=o>::</span><span class=n>parse</span><span class=p>()</span></code> does not have. It keeps an index entry at 16 bytes: offsets and counts are 32 bits wide. The limit is 16 bytes below 2^32, because the parser reads in 16-byte steps, and a position one step past the end must still fit.</li> <li><strong>A stream is always read to its end.</strong> There is no partial/streaming read of an <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>istream</span></code>: the stream is read to its end into a buffer the document owns, and parsing starts after that.</li> <li><strong>No source positions on <code>materialize()</code>.</strong> Even with <a href=../../api/macros/json_diagnostic_positions/ ><code>JSON_DIAGNOSTIC_POSITIONS</code></a> enabled, <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a> does not set them. The index has the start offset of every value, but not the end of arrays, objects, and escaped strings, which the positions also need.</li> <li><strong>Objects iterate in document order.</strong> <a href=../../api/basic_json_view/begin/ ><code>begin()</code></a>/ <a href=../../api/basic_json_view/end/ ><code>end()</code></a> and <a href=../../api/basic_json_view/items/ ><code>items()</code></a> visit an object's members in the order they appear in the source text. <code>basic_json</code>'s default <code>object_t</code> is a <code>std::map</code>, which sorts by key, so iterating a <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a>d value can print members in a different order than iterating the view they came from.</li> <li><strong>Chained access is safe.</strong> <a href=../../api/basic_json_view/operLine truncated
<span class=n>json_view</span><span class=w> </span><span class=n>last</span><span class=p>;</span>
<span class=k>for</span><span class=w> </span><span class=p>(</span><span class=k>const</span><span class=w> </span><span class=k>auto</span><span class=w> </span><span class=n>item</span><span class=w> </span><span class=o>:</span><span class=w> </span><span class=n>view</span><span class=p>.</span><span class=n>items</span><span class=p>())</span>
<span class=p>{</span>
@@ -62,7 +62,7 @@
<span class=w> </span><span class=n>last</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>item</span><span class=p>.</span><span class=n>value</span><span class=p>();</span>
<span class=w> </span><span class=p>}</span>
<span class=p>}</span>
</code></pre></div> <p>If you cannot trust the source text to have unique keys, check <a href=../../api/basic_json_view/size/ ><code>size()</code></a> against the number of distinct keys, or reject duplicates when iterating.</p> <h2 id=getting-values-out-without-copying>Getting values out without copying<a class=headerlink href=#getting-values-out-without-copying title="Permanent link">&para;</a></h2> <p><a href=../../api/basic_json_view/get/ ><code>get&lt;T&gt;()</code></a> converts most <code>T</code> directly from the flat index, without ever building a <code>basic_json</code> value for the conversion: <code class=highlight><span class=kt>bool</span></code>, arithmetic types, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>nullptr_t</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code>/other <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>basic_string</span></code>s (copied once), <code>basic_json_view</code> itself, the standard containers and other types the library converts (<code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>vector</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>map</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>list</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>array</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>pair</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>tuple</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>optional</span></code>, ...), and the types of the <code>NLOHMANN_DEFINE_TYPE_*</code> and <code>NLOHMANN_JSON_SERIALIZE_ENUM</code> macros. Types whose <code>from_json()</code> is written for <code>basic_json</code> only go through <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a><code>.get&lt;T&gt;()</code> instead: the subtree is built into a real <code>basic_json</code> value first, exactly as <a href=../../api/basic_json/parse/ ><code>parse()</code></a> would, and converted from there. See <a href=#writing-generic-code>Writing generic code</a> for how to write a <code>from_json()</code> that reads views.</p> <p>Two conversions never copy at all:</p> <ul> <li><a href=../../api/basic_json_view/get_string/ ><code>get_string()</code></a> (equivalently, <code class=highlight><span class=n>get</span><span class=o>&lt;</span><span class=n>string_view_t</span><span class=o>&gt;</span><span class=p>()</span></code>) returns a string as a <code>string_view_t</code> pointing into the document's <a href=../../api/basic_json_document/source/ ><code>source()</code></a> text -- or, for a string that contains escape sequences, into the document's own buffer of decoded strings -- instead of allocating a new <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code>.</li> <li><a href=../../api/basic_json_view/number_token/ ><code>number_token()</code></a> returns a number exactly as it was written in the source, e.g. <code class=highlight><span class=s>"1.50"</span></code>, <code class=highlight><span class=s>"1E2"</span></code>, or an integer with more digits than any number type holds, instead of rounding it into a <code class=highlight><span class=kt>double</span></code>/<code class=highlight><span class=kt>int64_t</span></code> the way <code class=highlight><span class=n>get</span><span class=o>&lt;</span><span class=n>T</span><span class=o>&gt;</span><span class=p>()</span></code> (and <a href=../../api/basic_json/parse/ ><code>basic_json::parse()</code></a>) would.</li> </ul> <p>Both results are only valid as long as the view -- and, for a string with no escapes, the borrowed source text -- is.</p> <h2 id=writing-generic-code>Writing generic code<a class=headerlink href=#writing-generic-code title="Permanent link">&para;</a></h2> <p>A view's type names its <code>BasicJsonType</code> and its <a href=../../api/view_mode/ ><code>view_mode</code></a>: <a href=../../api/json_view/ ><code>json_view</code></a> is <code class=highlight><span class=n>basic_json_view</span><span class=o>&lt;</span><span class=n>json</span><span class=p>,</span><span class=w> </span><span class=n>view_mode</span><span class=o>::</span><span class=n>read_only</span><span class=o>&gt;</span></code>, and <a href=../../api/json_editable_view/ ><code>json_editable_view</code></a> is <code class=highlight><span class=n>basic_json_view</span><span class=o>&lt;</span><span class=n>json</span><span class=p>,</span><span class=w> </span><span class=n>view_mode</span><span class=o>::</span><span class=n>editable</span><span class=o>&gt;</span></code>. These are different types. TLine truncated
</code></pre></div> <p>If you cannot trust the source text to have unique keys, check <a href=../../api/basic_json_view/size/ ><code>size()</code></a> against the number of distinct keys, or reject duplicates when iterating.</p> <h2 id=getting-values-out-without-copying>Getting values out without copying<a class=headerlink href=#getting-values-out-without-copying title="Permanent link">&para;</a></h2> <p><a href=../../api/basic_json_view/get/ ><code>get&lt;T&gt;()</code></a> converts most <code>T</code> directly from the flat index, without ever building a <code>basic_json</code> value for the conversion: <code class=highlight><span class=kt>bool</span></code>, arithmetic types, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>nullptr_t</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code>/other <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>basic_string</span></code>s (copied once), <code>basic_json_view</code> itself, the standard containers and other types the library converts (<code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>vector</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>map</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>list</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>array</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>pair</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>tuple</span></code>, <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>optional</span></code>, ...), and the types of the <code>NLOHMANN_DEFINE_TYPE_*</code> and <code>NLOHMANN_JSON_SERIALIZE_ENUM</code> macros. Types whose <code>from_json()</code> is written for <code>basic_json</code> only go through <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a><code>.get&lt;T&gt;()</code> instead: the subtree is built into a real <code>basic_json</code> value first, exactly as <a href=../../api/basic_json/parse/ ><code>parse()</code></a> would, and converted from there. See <a href=#writing-generic-code>Writing generic code</a> for how to write a <code>from_json()</code> that reads views.</p> <p>Two conversions never copy at all:</p> <ul> <li><a href=../../api/basic_json_view/get_string/ ><code>get_string()</code></a> (equivalently, <code class=highlight><span class=n>get</span><span class=o>&lt;</span><span class=n>string_view_t</span><span class=o>&gt;</span><span class=p>()</span></code>) returns a string as a <code>string_view_t</code> pointing into the document's <a href=../../api/basic_json_document/source/ ><code>source()</code></a> text — or, for a string that contains escape sequences, into the document's own buffer of decoded strings — instead of allocating a new <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>string</span></code>.</li> <li><a href=../../api/basic_json_view/number_token/ ><code>number_token()</code></a> returns a number exactly as it was written in the source, e.g. <code class=highlight><span class=s>"1.50"</span></code>, <code class=highlight><span class=s>"1E2"</span></code>, or an integer with more digits than any number type holds, instead of rounding it into a <code class=highlight><span class=kt>double</span></code>/<code class=highlight><span class=kt>int64_t</span></code> the way <code class=highlight><span class=n>get</span><span class=o>&lt;</span><span class=n>T</span><span class=o>&gt;</span><span class=p>()</span></code> (and <a href=../../api/basic_json/parse/ ><code>basic_json::parse()</code></a>) would.</li> </ul> <p>Both results are only valid as long as the view — and, for a string with no escapes, the borrowed source text — is.</p> <h2 id=writing-generic-code>Writing generic code<a class=headerlink href=#writing-generic-code title="Permanent link">&para;</a></h2> <p>A view's type names its <code>BasicJsonType</code> and its <a href=../../api/view_mode/ ><code>view_mode</code></a>: <a href=../../api/json_view/ ><code>json_view</code></a> is <code class=highlight><span class=n>basic_json_view</span><span class=o>&lt;</span><span class=n>json</span><span class=p>,</span><span class=w> </span><span class=n>view_mode</span><span class=o>::</span><span class=n>read_only</span><span class=o>&gt;</span></code>, and <a href=../../api/json_editable_view/ ><code>json_editable_view</code></a> is <code class=highlight><span class=n>basic_json_view</span><span class=o>&lt;</span><span class=n>json</span><span class=p>,</span><span class=w> </span><span class=n>view_mode</span><span class=o>::</span><span class=n>editable</span><span class=o>&gt;</span></code>. These are different typeLine truncated
<span class=kt>void</span><span class=w> </span><span class=nf>print_names</span><span class=p>(</span><span class=n>json_editable_view</span><span class=w> </span><span class=n>v</span><span class=p>);</span><span class=w> </span><span class=c1>// also accepts a json_view, but no ordered_json_view</span>
</code></pre></div> <p>This is the same pitfall as a <code>to_json</code>/<code>from_json</code> written for <code>json</code> instead of any <code>BasicJsonType</code>, which then does not work for <code>ordered_json</code>. The shortcuts are for declaring documents and views; a function that takes a view names the views it accepts with a template:</p> <div class=highlight><pre><span></span><code><span class=c1>// views of editable documents only</span>
<span class=k>template</span><span class=o>&lt;</span><span class=k>typename</span><span class=w> </span><span class=nc>BasicJsonType</span><span class=o>&gt;</span>
@@ -82,7 +82,19 @@
<span class=w> </span><span class=n>p</span><span class=p>.</span><span class=n>name</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>j</span><span class=p>.</span><span class=n>at</span><span class=p>(</span><span class=s>"name"</span><span class=p>).</span><span class=k>template</span><span class=w> </span><span class=n>get</span><span class=o>&lt;</span><span class=n>std</span><span class=o>::</span><span class=n>string</span><span class=o>&gt;</span><span class=p>();</span>
<span class=w> </span><span class=n>p</span><span class=p>.</span><span class=n>age</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>j</span><span class=p>.</span><span class=n>at</span><span class=p>(</span><span class=s>"age"</span><span class=p>).</span><span class=k>template</span><span class=w> </span><span class=n>get</span><span class=o>&lt;</span><span class=kt>int</span><span class=o>&gt;</span><span class=p>();</span>
<span class=p>}</span>
</code></pre></div> <p>Its body may only use what views and <code>basic_json</code> have in common: the type checks, <code>at()</code>, <code>operator[]</code>, <code>contains()</code>, <code>value()</code>, <code>size()</code>, iteration and <code>items()</code>, and <code>get&lt;T&gt;()</code>/<code>get_to()</code>. An unconstrained <code class=highlight><span class=k>template</span><span class=o>&lt;</span><span class=k>typename</span><span class=w> </span><span class=nc>BasicJsonType</span><span class=o>&gt;</span><span class=w> </span><span class=kt>void</span><span class=w> </span><span class=n>from_json</span><span class=p>(</span><span class=k>const</span><span class=w> </span><span class=n>BasicJsonType</span><span class=o>&amp;</span><span class=w> </span><span class=n>j</span><span class=p>,</span><span class=w> </span><span class=n>person</span><span class=o>&amp;</span><span class=w> </span><span class=n>p</span><span class=p>)</span></code>, as in <a href=../arbitrary_types/ >arbitrary type conversions</a>, is never called with a view, as it might use <code>basic_json</code> functions a view does not have; such a type is converted through <code>materialize()</code>. A <code>from_json()</code> for <code>json</code> only is converted through <code>materialize()</code> as well.</p> <p>Two things differ when a <code>from_json()</code> reads the view:</p> <ul> <li><strong>Duplicate keys.</strong> Members looked up by name (<code>at()</code>, <code>value()</code>, ... and thus the macros) find the <strong>first</strong> member with a repeated key, see <a href=#duplicate-keys>Duplicate keys</a>; <code class=highlight><span class=n>materialize</span><span class=p>().</span><span class=n>get</span><span class=o>&lt;</span><span class=n>T</span><span class=o>&gt;</span><span class=p>()</span></code> would see the last one. Containers built from an object, such as <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>map</span></code>, keep the <strong>last</strong> value either way, as <a href=../../api/basic_json/parse/ ><code>parse()</code></a> does.</li> <li><strong><a href=../../api/macros/json_diagnostics/ ><code>JSON_DIAGNOSTICS</code></a>.</strong> Exceptions thrown by the view carry no path to the value.</li> </ul> <p><code>to_json()</code> is unaffected: it writes <code>basic_json</code> values, never views.</p> <h2 id=writing-a-view-back>Writing a view back<a class=headerlink href=#writing-a-view-back title="Permanent link">&para;</a></h2> <p><a href=../../api/basic_json_view/dump/ ><code>dump()</code></a> serializes a view directly from the flat index, without ever building a <code>basic_json</code> value. An object's members are written in document order, not sorted by key, and <em>every</em> occurrence of a repeated key is written, not only the last one -- the same two ways <a href=#what-is-different>iteration</a> already differs from a <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a>d value, see <a href=#duplicate-keys>Duplicate keys</a>. <code class=highlight><span class=n>materialize</span><span class=p>().</span><span class=n>dump</span><span class=p>()</span></code> gives a different result in both respects for a <code>json_view</code>.</p> <p>By default, numbers are written the way <a href=../../api/basic_json/dump/ ><code>basic_json::dump()</code></a> would. <a href=../../api/basic_json_view/number_format/ ><code>number_format::source</code></a> instead copies every number exactly as it was written in the source text -- a price like <code class=highlight><span class=mf>19.90</span></code>, a long order or account ID with more digits than any number type holds, or a high-precision coordinate -- something <code>basic_json</code> cannot do at all, since parsing already reduces a number to its parsed <code class=highlight><span class=kt>double</span></code>/<code class=highlight><span class=kt>int64_t</span></code> value.</p> <p><a href=../../api/basic_json_view/operator_ltlt/ ><code>operator&lt;&lt;</code></a> writes a view to a stream the way <code>basic_json</code>'s does, using the stream's <code>width</code>/<code>fill</code> for indentation.</p> <h2 id=editing-a-document>Editing a document<a class=headerlink href=#editing-a-document title="Permanent link">&para;</a></h2> <p>Everything above is read-only: a <code>json_document</code>/<code>json_view</code> lets you look at a parsed text without copying it, but not change it. <a href=../../api/basic_json_document/ ><code>basic_json_document&lt;BasicJsonType, view_mode::editable&gt;</code></a> -- more conveniently spelled <a href=../../api/json_editable_document/ ><code>json_editable_document</code></a> or <a href=../../api/ordered_json_editable_document/ ><code>ordered_json_editable_document</code></a> -- also lets you <a href=../../api/basic_json_document/set/ ><code>set</code></a> a value, <a href=../../api/basic_json_document/push_back/ ><code>push_back</code></a> onto orLine truncated
</code></pre></div> <p>Its body may only use what views and <code>basic_json</code> have in common: the type checks, <code>at()</code>, <code>operator[]</code>, <code>contains()</code>, <code>value()</code>, <code>size()</code>, iteration and <code>items()</code>, and <code>get&lt;T&gt;()</code>/<code>get_to()</code>. An unconstrained <code class=highlight><span class=k>template</span><span class=o>&lt;</span><span class=k>typename</span><span class=w> </span><span class=nc>BasicJsonType</span><span class=o>&gt;</span><span class=w> </span><span class=kt>void</span><span class=w> </span><span class=n>from_json</span><span class=p>(</span><span class=k>const</span><span class=w> </span><span class=n>BasicJsonType</span><span class=o>&amp;</span><span class=w> </span><span class=n>j</span><span class=p>,</span><span class=w> </span><span class=n>person</span><span class=o>&amp;</span><span class=w> </span><span class=n>p</span><span class=p>)</span></code>, as in <a href=../arbitrary_types/ >arbitrary type conversions</a>, is never called with a view, as it might use <code>basic_json</code> functions a view does not have; such a type is converted through <code>materialize()</code>. A <code>from_json()</code> for <code>json</code> only is converted through <code>materialize()</code> as well.</p> <p>Two things differ when a <code>from_json()</code> reads the view:</p> <ul> <li><strong>Duplicate keys.</strong> Members looked up by name (<code>at()</code>, <code>value()</code>, ... and thus the macros) find the <strong>first</strong> member with a repeated key, see <a href=#duplicate-keys>Duplicate keys</a>; <code class=highlight><span class=n>materialize</span><span class=p>().</span><span class=n>get</span><span class=o>&lt;</span><span class=n>T</span><span class=o>&gt;</span><span class=p>()</span></code> would see the last one. Containers built from an object, such as <code class=highlight><span class=n>std</span><span class=o>::</span><span class=n>map</span></code>, keep the <strong>last</strong> value either way, as <a href=../../api/basic_json/parse/ ><code>parse()</code></a> does.</li> <li><strong><a href=../../api/macros/json_diagnostics/ ><code>JSON_DIAGNOSTICS</code></a>.</strong> Exceptions thrown by the view carry no path to the value.</li> </ul> <p><code>to_json()</code> is unaffected: it writes <code>basic_json</code> values, never views.</p> <h2 id=writing-a-view-back>Writing a view back<a class=headerlink href=#writing-a-view-back title="Permanent link">&para;</a></h2> <p><a href=../../api/basic_json_view/dump/ ><code>dump()</code></a> serializes a view directly from the flat index, without ever building a <code>basic_json</code> value. An object's members are written in document order, not sorted by key, and <em>every</em> occurrence of a repeated key is written, not only the last one — the same two ways <a href=#what-is-different>iteration</a> already differs from a <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a>d value, see <a href=#duplicate-keys>Duplicate keys</a>. <code class=highlight><span class=n>materialize</span><span class=p>().</span><span class=n>dump</span><span class=p>()</span></code> gives a different result in both respects for a <code>json_view</code>.</p> <p>By default, numbers are written the way <a href=../../api/basic_json/dump/ ><code>basic_json::dump()</code></a> would. <a href=../../api/basic_json_view/number_format/ ><code>number_format::source</code></a> instead copies every number exactly as it was written in the source text — a price like <code class=highlight><span class=mf>19.90</span></code>, a long order or account ID with more digits than any number type holds, or a high-precision coordinate — something <code>basic_json</code> cannot do at all, since parsing already reduces a number to its parsed <code class=highlight><span class=kt>double</span></code>/<code class=highlight><span class=kt>int64_t</span></code> value.</p> <p><a href=../../api/basic_json_view/operator_ltlt/ ><code>operator&lt;&lt;</code></a> writes a view to a stream the way <code>basic_json</code>'s does, using the stream's <code>width</code>/<code>fill</code> for indentation.</p> <h2 id=editing-a-document>Editing a document<a class=headerlink href=#editing-a-document title="Permanent link">&para;</a></h2> <p>Everything above is read-only: a <code>json_document</code>/<code>json_view</code> lets you look at a parsed text without copying it, but not change it. <a href=../../api/basic_json_document/ ><code>basic_json_document&lt;BasicJsonType, view_mode::editable&gt;</code></a> — more conveniently spelled <a href=../../api/json_editable_document/ ><code>json_editable_document</code></a> or <a href=../../api/ordered_json_editable_document/ ><code>ordered_json_editable_document</code></a> — also lets you <a href=../../api/basic_json_document/set/ ><code>set</code></a> a value, <a href=../../api/basic_json_document/push_back/ ><code>push_back</code></a> onLine truncated
"name":"cache",
"port":6379,
"price":<span class=nj-jsonview-bad>19.9</span>,
"retries":3}</pre></div> <div><p class=nj-jsonview-cmp-h><code>ordered_json</code></p><p class=nj-jsonview-cmp-v>key order kept, numbers rewritten</p><pre>{"name":"cache",
"host":"db2",
"port":6379,
"price":<span class=nj-jsonview-bad>19.9</span>,
"retries":3}</pre></div> <div class=nj-jsonview-cmp--best><p class=nj-jsonview-cmp-h><code>json_editable_document</code></p><p class=nj-jsonview-cmp-v>order and spelling kept</p><pre>{"name":"cache",
"host":"db2",
"port":6379,
"price":<span class=nj-jsonview-ok>19.90</span>,
"retries":3}</pre></div> </div> <p>An editable document keeps both. <a href=../../api/basic_json_view/dump/ ><code>dump()</code></a> of an edited document writes members in document order — a member <a href=../../api/basic_json_document/set/ ><code>set</code></a> added goes at the end, exactly where it was inserted, and an <a href=../../api/basic_json_document/erase/ ><code>erase</code></a>d member simply leaves a gap: everything around it keeps its place — and <a href=../../api/basic_json_view/number_format/ ><code>number_format::source</code></a> keeps the exact spelling of every number an edit did not itself touch; a number an edit <em>did</em> touch is written the way <a href=../../api/basic_json/dump/ ><code>BasicJsonType::dump()</code></a> would write it, since there is no source spelling for a brand new value.</p> <details class=example> <summary>Example: patch a configuration, keeping member order and number spellings</summary> <div class=highlight><pre><span></span><code><span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;iostream&gt;</span>
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;nlohmann/json_view.hpp&gt;</span>
<span class=k>using</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json</span><span class=p>;</span>
@@ -116,7 +128,7 @@
<span class=p>{</span><span class=nt>"name"</span><span class=p>:</span><span class=s2>"cache"</span><span class=p>,</span><span class=nt>"host"</span><span class=p>:</span><span class=s2>"db2"</span><span class=p>,</span><span class=nt>"port"</span><span class=p>:</span><span class=mi>6379</span><span class=p>,</span><span class=nt>"price"</span><span class=p>:</span><span class=mf>19.90</span><span class=p>,</span><span class=nt>"retries"</span><span class=p>:</span><span class=mi>3</span><span class=p>}</span>
<span class=p>{</span><span class=nt>"host"</span><span class=p>:</span><span class=s2>"db2"</span><span class=p>,</span><span class=nt>"name"</span><span class=p>:</span><span class=s2>"cache"</span><span class=p>,</span><span class=nt>"port"</span><span class=p>:</span><span class=mi>6379</span><span class=p>,</span><span class=nt>"price"</span><span class=p>:</span><span class=mf>19.9</span><span class=p>,</span><span class=nt>"retries"</span><span class=p>:</span><span class=mi>3</span><span class=p>}</span>
</code></pre></div> </details> <h3 id=what-stays-valid-and-what-an-edit-costs>What stays valid, and what an edit costs<a class=headerlink href=#what-stays-valid-and-what-an-edit-costs title="Permanent link">&para;</a></h3> <p>The source text itself is <strong>never written</strong>, and the parsed index never moves -- a value keeps the node it was parsed into for as long as it is not itself replaced. So every <a href=../../api/basic_json_view/ >view</a> taken before an edit, including a previously obtained <a href=../../api/basic_json_document/root/ ><code>root()</code></a>, stays valid and, if it still refers to the edited value, sees the edit; a view of a value a later edit drops or replaces just keeps showing what it last held. New values go to storage the document allocates and owns on demand. The one thing an edit does invalidate is the <strong>iterators</strong> taken over an edited array or object: the first time one of its elements is set, appended to, inserted into, or erased, its elements move from the parsed, fixed layout to a growable block of links so that <a href=../../api/basic_json_document/push_back/ ><code>push_back</code></a> can later grow it in amortized constant time -- existing elements are not touched, but an iterator that was walking the old layout no longer matches. A string obtained with <a href=../../api/basic_json_view/get_string/ ><code>get_string()</code></a> is unaffected either way and stays valid across further edits. See <a href=../../api/basic_json_document/#edits><code>basic_json_document</code>'s Edits</a> for the details, and <a href=../../api/basic_json_document/set/#exception-safety><code>set</code>'s Exception safety</a> for what an edit guarantees if it throws (the <em>basic</em> guarantee, not the strong one <code>dump()</code> and the read-only functions provide). How edits are kept in the index is described in the <a href=../../home/architecture/#node-index-of-json-views>architecture overview</a>.</p> <h2 id=images>Images<a class=headerlink href=#images title="Permanent link">&para;</a></h2> <p><a href=../../api/basic_json_document/save/ ><code>save()</code></a> writes a document as an <em>image</em>: a byte buffer that <a href=../../api/basic_json_document/load/ ><code>load()</code></a> reads back into a document without parsing -- no lexing, no building the node index, nothing but copying the nodes and pointing the text and the decoded strings at the image. Where <a href=../../api/basic_json_document/parse_copy/ ><code>parse_copy()</code></a> still has to scan the whole input, <a href=../../api/basic_json_document/load/ ><code>load()</code></a> turns that scan into a copy of the node index alone.</p> <p><strong>Why.</strong> A document that is parsed once and then read many times -- a configuration loaded at startup, a template rendered on every request, a large reference dataset a worker process needs in memory -- pays for parsing once but can amortize <a href=../../api/basic_json_document/save/ ><code>save()</code></a>'s cost across every later load. That makes images useful for a cache: save a document the first time it is parsed (to a file, a shared-memory segment, an in-process cache), and <a href=../../api/basic_json_document/load/ ><code>load()</code></a> it on every later use instead of parsing the source text again. They are just as useful for handing a parsed document to another process (or a forked worker) running the same build of the library, since <a href=../../api/basic_json_document/load/ ><code>load()</code></a> turns the transfer into a copy of the node index plus pointers into the received bytes, not a re-parse.</p> <p><strong>Choosing a check.</strong> <a href=../../api/basic_json_document/load/ ><code>load()</code></a> takes an <a href=../../api/basic_json_document/load/#image_check><code>image_check</code></a> that trades validation against speed: <code>image_check::full</code> (the default) checks everything the parser itself guarantees, so a checked image is exactly as safe to read and serialize as a freshly parsed document -- the right choice whenever the image did not come straight from this process's own <a href=../../api/basic_json_document/save/ ><code>save()</code></a>, such as a file or a network peer. <code>image_check::bounds</code> only checks structure and bounds -- cheaper, since it skips scanning the text and the decoded strings -- and fits a cache the process trusts, one it wrote and reads back itself. <code>image_check::none</code> skips validation entirely, for an image trusted as much as the process's own memory. See <a href=../../api/basic_json_document/load/#notes><code>load()</code>'s Notes</a> for exactly what each level does and does not guarantee.</p> <details class=example> <summary>Example: cache a parsed configuration as an image</summary> <div class=highlight><pre><span></span><code><span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;cstdint&gt;</span>
</code></pre></div> </details> <h3 id=what-stays-valid-and-what-an-edit-costs>What stays valid, and what an edit costs<a class=headerlink href=#what-stays-valid-and-what-an-edit-costs title="Permanent link">&para;</a></h3> <p>The source text itself is <strong>never written</strong>, and the parsed index never moves — a value keeps the node it was parsed into for as long as it is not itself replaced. So every <a href=../../api/basic_json_view/ >view</a> taken before an edit, including a previously obtained <a href=../../api/basic_json_document/root/ ><code>root()</code></a>, stays valid and, if it still refers to the edited value, sees the edit; a view of a value a later edit drops or replaces just keeps showing what it last held. New values go to storage the document allocates and owns on demand. The one thing an edit does invalidate is the <strong>iterators</strong> taken over an edited array or object: the first time an element is added to it, inserted into it, erased from it, or replaced by a value that spans a different number of nodes, its elements move from the parsed, fixed layout to a growable block of links so that <a href=../../api/basic_json_document/push_back/ ><code>push_back</code></a> can later grow it in amortized constant time — existing elements are not touched, but an iterator that was walking the old layout no longer matches. A string obtained with <a href=../../api/basic_json_view/get_string/ ><code>get_string()</code></a> is unaffected either way and stays valid across further edits. See <a href=../../api/basic_json_document/#edits><code>basic_json_document</code>'s Edits</a> for the details, and <a href=../../api/basic_json_document/set/#exception-safety><code>set</code>'s Exception safety</a> for what an edit guarantees if it throws (the <em>basic</em> guarantee, not the strong one <code>dump()</code> and the read-only functions provide). How edits are kept in the index is described in the <a href=../../home/architecture/#node-index-of-json-views>architecture overview</a>.</p> <p>Step through the edits of a small document (the node fields follow the <a href=../../home/architecture/#node-index-of-json-views>architecture overview</a>):</p> <div class=nj-jsonview-steps data-nj-jsonview-steps> <figure class=nj-jsonview-step data-label="After parsing"> <div class=nj-jsonview-scroll> <svg class="nj-fig nj-jsonview nj-jsonview-editfig" viewbox="0 0 840 372" role=img aria-labelledby="fig-jve0-title fig-jve0-title-d" xmlns=http://www.w3.org/2000/svg> <title id=fig-jve0-title>Step 1 of 5: parsed</title> <desc id=fig-jve0-title-d>After parse: nine nodes, nothing owned yet. The dump with number_format::source is {&quot;port&quot;:6379,&quot;price&quot;:19.90,&quot;tags&quot;:[&quot;eu&quot;,&quot;a\&quot;b&quot;]}.</desc> <defs> <marker id=jve0-line viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-line nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> <marker id=jve0-own viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-own nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> <marker id=jve0-struct viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-struct nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> <marker id=jve0-bad viewbox="0 0 10 10" refx=9 refy=5 markerwidth=6.5 markerheight=6.5 orient=auto-start-reverse><path class="nj-jsonview-c-bad nj-jsonview-head" d="M0 1 L10 5 L0 9 z"/></marker> </defs> <text class="nj-jsonview-note nj-jsonview-strong" x=16 y=22 xml:space=preserve>parsed index: fixed, never moves</text> <text class=nj-jsonview-note x=824 y=22 text-anchor=end xml:space=preserve>the source text is never written</text> <rect x=36 y=34 width=84 height=50 rx=5 class="nj-jsonview-node nj-jsonview-c-struct"/> <text class=nj-jsonview-tiny x=42 y=47 xml:space=preserve>0</text> <text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-struct" x=78 y=61 text-anchor=middle xml:space=preserve>object</text> <text class="nj-jsonview-mono nj-jsonview-sm" x=78 y=77 text-anchor=middle xml:space=preserve>{ } 3</text> <rect x=124 y=34 width=84 height=50 rx=5 class="nj-jsonview-node nj-jsonview-c-key"/> <text class=nj-jsonview-tiny x=130 y=47 xml:space=preserve>1</text> <text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-key" x=166 y=61 text-anchor=middle xml:space=preserve>key</text> <text class="nj-jsonview-mono nj-jsonview-sm" x=166 y=77 text-anchor=middle xml:space=preserve>port</text> <rect x=212 y=34 width=84 height=50 rx=5 class="nj-jsonview-node nj-jsonview-c-num"/> <text class=nj-jsonview-tiny x=218 y=47 xml:space=preserve>2</text> <text class="nj-jsonview-kind nj-jsonview-xs nj-jsonview-c-num" x=254 y=61 text-anchor=middle xml:space=preserve>uint</text> <text class="nj-jsonview-mono nj-jsonview-sm" x=254 y=77 text-anchor=middle xml:space=preserve>6379</text> <rect x=300 Line truncated
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;iostream&gt;</span>
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;string&gt;</span>
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;vector&gt;</span>
@@ -149,4 +161,4 @@
</code></pre></div> <p>Output:</p> <div class=highlight><pre><span></span><code><span class=mi>316</span>
<span class=kc>true</span>
<span class=kc>true</span>
</code></pre></div> </details> <div class="admonition warning"> <p class=admonition-title>Experimental</p> <p>The image format is versioned but not yet stable, and may change in an incompatible way before it is declared stable. It is little-endian only (on big-endian targets, <code>save()</code> and <code>load()</code> do not exist), and tied to the library build that wrote it -- use it to cache a document or to hand one to another process running the <em>same</em> build, not as a long-term storage format; keep the original <abbr title="JavaScript Object Notation">JSON</abbr> text if a saved document needs to be readable by a future library version.</p> </div> <p>The idea of a document you can read without parsing comes from zero-copy formats such as <a href="https://github.com/google/flatbuffers">FlatBuffers</a> and <a href="https://github.com/yandex/yaff">YaFF</a>; the check <a href=../../api/basic_json_document/load/ ><code>load()</code></a> runs follows the idea of FlatBuffers' Verifier. No code is taken from either.</p> <h2 id=choosing-between-json-ordered_json-the-sax-interface-and-json_view>Choosing between <code>json</code>, <code>ordered_json</code>, the <abbr title="Simple API for XML">SAX</abbr> interface, and <code>json_view</code><a class=headerlink href=#choosing-between-json-ordered_json-the-sax-interface-and-json_view title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th></th> <th><a href=../../api/json/ ><code>json</code></a> / <a href=../../api/ordered_json/ ><code>ordered_json</code></a></th> <th><a href=../parsing/sax_interface/ ><abbr title="Simple API for XML">SAX</abbr> interface</a></th> <th><a href=../../api/json_document/ ><code>json_document</code></a> / <a href=../../api/json_view/ ><code>json_view</code></a></th> <th><a href=../../api/json_editable_document/ ><code>json_editable_document</code></a> / <a href=../../api/json_editable_view/ ><code>json_editable_view</code></a></th> </tr> </thead> <tbody> <tr> <td><strong>Ownership</strong></td> <td>owns every value</td> <td>owns nothing; you decide what to keep, in your handler</td> <td>borrows or owns the <em>text</em>; the index is always owned by the document</td> <td>same as <code>json_document</code>; edits go to storage the document owns</td> </tr> <tr> <td><strong>Mutability</strong></td> <td>freely mutable</td> <td>not applicable (a one-shot event stream)</td> <td>read-only</td> <td><a href=../../api/basic_json_document/set/ ><code>set</code></a>/<a href=../../api/basic_json_document/push_back/ ><code>push_back</code></a>/<a href=../../api/basic_json_document/insert/ ><code>insert</code></a>/<a href=../../api/basic_json_document/erase/ ><code>erase</code></a> edit in place; the source text is never rewritten</td> </tr> <tr> <td><strong>What you get</strong></td> <td>a full tree you can read, write, and keep as long as you like</td> <td>a sequence of callbacks; whatever your handler builds from them</td> <td>a flat index plus, on demand, <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a>d <code>json</code>/<code>ordered_json</code> values for the parts you actually use</td> <td>the same, plus <a href=../../api/basic_json_view/dump/ ><code>dump()</code></a> of an edited document that keeps the member order and, with <a href=../../api/basic_json_view/number_format/ ><code>number_format::source</code></a>, the spelling of every untouched number</td> </tr> <tr> <td><strong>Typical use</strong></td> <td>general-purpose <abbr title="JavaScript Object Notation">JSON</abbr> handling: config, request/response bodies you build or modify, anything you hold onto</td> <td>validating or projecting a text into your own data structure without ever holding the whole thing as <abbr title="JavaScript Object Notation">JSON</abbr></td> <td>large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives</td> <td>a document you read, patch a few fields of, and write back -- a configuration file, for instance -- where the rest of it should come back exactly as it was</td> </tr> <tr> <td><strong>Caching/reload</strong></td> <td>not applicable -- re-parse, or roll your own serialization</td> <td>not applicable</td> <td><a href=../../api/basic_json_document/save/ ><code>save()</code></a>/<a href=../../api/basic_json_document/load/ ><code>load()</code></a>: cache the parsed index as an image and reload it without parsing</td> <td>same, saving the document's current -- possibly edited -- state</td> </tr> </tbody> </table> <h2 id=version-history>Version history<a class=headerlink href=#version-history title="Permanent link">&para;</a></h2> <ul> <li>Added in version 3.13.0 <span class=unreleased-version title="Not part of a release yet; the latest release is 3.12.0.">unreleased</span>.</li> </ul> <!-- https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-a-glossary --> <aside class=md-source-file> <spanLine truncated
</code></pre></div> </details> <div class="admonition warning"> <p class=admonition-title>Experimental</p> <p>The image format is versioned but not yet stable, and may change in an incompatible way before it is declared stable. It is little-endian only (on big-endian targets, <code>save()</code> and <code>load()</code> do not exist), and tied to the library build that wrote it — use it to cache a document or to hand one to another process running the <em>same</em> build, not as a long-term storage format; keep the original <abbr title="JavaScript Object Notation">JSON</abbr> text if a saved document needs to be readable by a future library version.</p> </div> <p>The idea of a document you can read without parsing comes from zero-copy formats such as <a href="https://github.com/google/flatbuffers">FlatBuffers</a> and <a href="https://github.com/yandex/yaff">YaFF</a>; the check <a href=../../api/basic_json_document/load/ ><code>load()</code></a> runs follows the idea of FlatBuffers' Verifier. No code is taken from either.</p> <h2 id=choosing-between-json-ordered_json-the-sax-interface-and-json_view>Choosing between <code>json</code>, <code>ordered_json</code>, the <abbr title="Simple API for XML">SAX</abbr> interface, and <code>json_view</code><a class=headerlink href=#choosing-between-json-ordered_json-the-sax-interface-and-json_view title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th></th> <th><a href=../../api/json/ ><code>json</code></a> / <a href=../../api/ordered_json/ ><code>ordered_json</code></a></th> <th><a href=../parsing/sax_interface/ ><abbr title="Simple API for XML">SAX</abbr> interface</a></th> <th><a href=../../api/json_document/ ><code>json_document</code></a> / <a href=../../api/json_view/ ><code>json_view</code></a></th> <th><a href=../../api/json_editable_document/ ><code>json_editable_document</code></a> / <a href=../../api/json_editable_view/ ><code>json_editable_view</code></a></th> </tr> </thead> <tbody> <tr> <td><strong>Ownership</strong></td> <td>owns every value</td> <td>owns nothing; you decide what to keep, in your handler</td> <td>borrows or owns the <em>text</em>; the index is always owned by the document</td> <td>same as <code>json_document</code>; edits go to storage the document owns</td> </tr> <tr> <td><strong>Mutability</strong></td> <td>freely mutable</td> <td>not applicable (a one-shot event stream)</td> <td>read-only</td> <td><a href=../../api/basic_json_document/set/ ><code>set</code></a>/<a href=../../api/basic_json_document/push_back/ ><code>push_back</code></a>/<a href=../../api/basic_json_document/insert/ ><code>insert</code></a>/<a href=../../api/basic_json_document/erase/ ><code>erase</code></a> edit in place; the source text is never rewritten</td> </tr> <tr> <td><strong>What you get</strong></td> <td>a full tree you can read, write, and keep as long as you like</td> <td>a sequence of callbacks; whatever your handler builds from them</td> <td>a flat index plus, on demand, <a href=../../api/basic_json_view/materialize/ ><code>materialize()</code></a>d <code>json</code>/<code>ordered_json</code> values for the parts you actually use</td> <td>the same, plus <a href=../../api/basic_json_view/dump/ ><code>dump()</code></a> of an edited document that keeps the member order and, with <a href=../../api/basic_json_view/number_format/ ><code>number_format::source</code></a>, the spelling of every untouched number</td> </tr> <tr> <td><strong>Typical use</strong></td> <td>general-purpose <abbr title="JavaScript Object Notation">JSON</abbr> handling: config, request/response bodies you build or modify, anything you hold onto</td> <td>validating or projecting a text into your own data structure without ever holding the whole thing as <abbr title="JavaScript Object Notation">JSON</abbr></td> <td>large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives</td> <td>a document you read, patch a few fields of, and write back — a configuration file, for instance — where the rest of it should come back exactly as it was</td> </tr> <tr> <td><strong>Caching/reload</strong></td> <td>not applicable — re-parse, or roll your own serialization</td> <td>not applicable</td> <td><a href=../../api/basic_json_document/save/ ><code>save()</code></a>/<a href=../../api/basic_json_document/load/ ><code>load()</code></a>: cache the parsed index as an image and reload it without parsing</td> <td>same, saving the document's current — possibly edited — state</td> </tr> </tbody> </table> <p class=nj-table-caption>Each column is one way to work with <abbr title="JavaScript Object Notation">JSON</abbr> in this library, and each row compares one property of them.</p> <h2 id=see-also>See also<a class=headerlink href=#see-also title="Permanent link">&para;</a></h2> <ul> <li><a href=../../api/basic_json_document/ ><code>basic_json_document</code></a> - the document that owns the parsLine truncated
+91 -20
View File
@@ -10,7 +10,11 @@
[`basic_json_document::parse()`](https://json.nlohmann.me/api/basic_json_document/parse/index.md) parses the same JSON grammar, with the same options, but instead of a tree it builds a flat index of the values it found: one [16-byte entry](https://json.nlohmann.me/home/architecture/#node-index-of-json-views) per value (and one per object key), in document order. Integers are converted while parsing and stored in their entry. Floating-point numbers and strings are referenced in place in the source text: a float is converted when it is read, and only strings that contain escape sequences are decoded, into one shared buffer owned by the document.
[`basic_json_view`](https://json.nlohmann.me/api/basic_json_view/index.md) is a small, trivially copyable handle (two pointers) into that index. It gives you the read-only, type-inspection part of the `basic_json` interface -- [`type()`](https://json.nlohmann.me/api/basic_json_view/type/index.md) and the `is_*()` predicates, [`size()`](https://json.nlohmann.me/api/basic_json_view/size/index.md)/[`empty()`](https://json.nlohmann.me/api/basic_json_view/empty/index.md) -- as well as element access ([`operator[]`](https://json.nlohmann.me/api/basic_json_view/operator%5B%5D/index.md), [`at`](https://json.nlohmann.me/api/basic_json_view/at/index.md), [`front`](https://json.nlohmann.me/api/basic_json_view/front/index.md)/[`back`](https://json.nlohmann.me/api/basic_json_view/back/index.md)), lookup ([`find`](https://json.nlohmann.me/api/basic_json_view/find/index.md), [`contains`](https://json.nlohmann.me/api/basic_json_view/contains/index.md), [`count`](https://json.nlohmann.me/api/basic_json_view/count/index.md)), and iteration ([`begin`](https://json.nlohmann.me/api/basic_json_view/begin/index.md)/[`end`](https://json.nlohmann.me/api/basic_json_view/end/index.md), [`items`](https://json.nlohmann.me/api/basic_json_view/items/index.md)) -- without ever allocating a `basic_json` value. When you do need an actual `basic_json` value for a subtree, [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) builds exactly the one [`parse()`](https://json.nlohmann.me/api/basic_json/parse/index.md) would have produced for it.
[`basic_json_view`](https://json.nlohmann.me/api/basic_json_view/index.md) is a small, trivially copyable handle (two pointers) into that index. It gives you the read-only, type-inspection part of the `basic_json` interface — [`type()`](https://json.nlohmann.me/api/basic_json_view/type/index.md) and the `is_*()` predicates, [`size()`](https://json.nlohmann.me/api/basic_json_view/size/index.md)/[`empty()`](https://json.nlohmann.me/api/basic_json_view/empty/index.md) — as well as element access ([`operator[]`](https://json.nlohmann.me/api/basic_json_view/operator%5B%5D/index.md), [`at`](https://json.nlohmann.me/api/basic_json_view/at/index.md), [`front`](https://json.nlohmann.me/api/basic_json_view/front/index.md)/[`back`](https://json.nlohmann.me/api/basic_json_view/back/index.md)), lookup ([`find`](https://json.nlohmann.me/api/basic_json_view/find/index.md), [`contains`](https://json.nlohmann.me/api/basic_json_view/contains/index.md), [`count`](https://json.nlohmann.me/api/basic_json_view/count/index.md)), and iteration ([`begin`](https://json.nlohmann.me/api/basic_json_view/begin/index.md)/[`end`](https://json.nlohmann.me/api/basic_json_view/end/index.md), [`items`](https://json.nlohmann.me/api/basic_json_view/items/index.md)) — without ever allocating a `basic_json` value. When you do need an actual `basic_json` value for a subtree, [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) builds exactly the one [`parse()`](https://json.nlohmann.me/api/basic_json/parse/index.md) would have produced for it.
The same small text, parsed both ways:
Top: the tree of separate heap allocations that `basic_json::parse()` builds. Bottom: `json_document::parse()` keeps the source text and builds one flat index of 16-byte nodes that point back into it, plus a buffer of decoded strings.
## How to use it
@@ -48,7 +52,7 @@ true
## Ownership and lifetime
A document either **borrows** the text it was parsed from, or **owns** its own copy of it; call [`owns_source()`](https://json.nlohmann.me/api/basic_json_document/owns_source/index.md) to find out which happened. [`parse()`](https://json.nlohmann.me/api/basic_json_document/parse/index.md) decides this from the value category and type of its argument (an lvalue `std::string` is borrowed; an rvalue `std::string` is moved in, owned without a copy; a stream is read into an owned buffer; and so on -- see [`parse`'s Notes](https://json.nlohmann.me/api/basic_json_document/parse/#notes) for the full table). [`parse_copy()`](https://json.nlohmann.me/api/basic_json_document/parse_copy/index.md) always owns a copy, regardless of the input.
A document either **borrows** the text it was parsed from, or **owns** its own copy of it; call [`owns_source()`](https://json.nlohmann.me/api/basic_json_document/owns_source/index.md) to find out which happened. [`parse()`](https://json.nlohmann.me/api/basic_json_document/parse/index.md) decides this from the value category and type of its argument (an lvalue `std::string` is borrowed; an rvalue `std::string` is moved in, owned without a copy; a stream is read into an owned buffer; and so on — see [`parse`'s Notes](https://json.nlohmann.me/api/basic_json_document/parse/#notes) for the full table). [`parse_copy()`](https://json.nlohmann.me/api/basic_json_document/parse_copy/index.md) always owns a copy, regardless of the input.
A borrowed document depends on your buffer
@@ -63,7 +67,9 @@ A view is valid only while all of the following hold:
Moving the `basic_json_document<>` itself is fine and does **not** invalidate its views: the index is a separate heap allocation that keeps its address across the move. Take a fresh view from [`root()`](https://json.nlohmann.me/api/basic_json_document/root/index.md) whenever any of the other conditions above was not met.
A view can dangle, exactly like a `std::string_view`: nothing stops you from using it after its document is gone. This is deliberate. A view is two pointers and trivially copyable; making it safe (a `std::shared_ptr` in the document and a `std::weak_ptr` in each view) would make it larger, add reference counting to every copy, and a `lock()` to every access -- exactly the costs the view exists to avoid. What is caught is the obvious case: because a view dies with its document, [`root()`](https://json.nlohmann.me/api/basic_json_document/root/index.md) is not callable on a temporary document: `auto v = json_document::parse(text).root();` does not compile. Give the document a name first.
Four timelines of a document and its views, each bar showing when an object is alive or valid and when it dangles. Destroying the borrowed text, re-parsing, or shrinking the document invalidates the views taken before; moving the document does not.
A view can dangle, exactly like a `std::string_view`: nothing stops you from using it after its document is gone. This is deliberate. A view is two pointers and trivially copyable; making it safe (a `std::shared_ptr` in the document and a `std::weak_ptr` in each view) would make it larger, add reference counting to every copy, and a `lock()` to every access — exactly the costs the view exists to avoid. What is caught is the obvious case: because a view dies with its document, [`root()`](https://json.nlohmann.me/api/basic_json_document/root/index.md) is not callable on a temporary document: `auto v = json_document::parse(text).root();` does not compile. Give the document a name first.
Example: borrowed and owned documents, and when views become invalid
@@ -115,7 +121,7 @@ true
## What is the same as `parse()`
- **Accept/reject.** [`accept()`](https://json.nlohmann.me/api/basic_json_document/accept/index.md) and [`parse()`](https://json.nlohmann.me/api/basic_json_document/parse/index.md) accept and reject exactly the same inputs as [`basic_json::accept()`](https://json.nlohmann.me/api/basic_json/accept/index.md)/[`basic_json::parse()`](https://json.nlohmann.me/api/basic_json/parse/index.md), with the same `ignore_comments` and `ignore_trailing_commas` options.
- **Errors.** A failing parse throws the same exception -- the same id, message, and position -- because on failure the library's own parser is run on the same bytes to produce the diagnostic. Valid input never pays for this, and the replay builds no values: it costs about a lexing pass up to the error. `allow_exceptions == false` skips the replay and gives a [discarded](https://json.nlohmann.me/api/basic_json_document/is_discarded/index.md) document instead of throwing, just as it gives a discarded value for `basic_json::parse()`.
- **Errors.** A failing parse throws the same exception — the same id, message, and position — because on failure the library's own parser is run on the same bytes to produce the diagnostic. Valid input never pays for this, and the replay builds no values: it costs about a lexing pass up to the error. `allow_exceptions == false` skips the replay and gives a [discarded](https://json.nlohmann.me/api/basic_json_document/is_discarded/index.md) document instead of throwing, just as it gives a discarded value for `basic_json::parse()`.
- **Number classification.** An integer literal that does not fit into the 64-bit integer type becomes a floating-point value, exactly as it does for `basic_json::parse()`.
- **Macros.** [`JSON_STRICT_NUL_HANDLING`](https://json.nlohmann.me/api/macros/json_strict_nul_handling/index.md) and [`JSON_NOEXCEPTION`](https://json.nlohmann.me/api/macros/json_noexception/index.md)/[`JSON_THROW_USER`](https://json.nlohmann.me/api/macros/json_throw_user/index.md) behave the same way they do for `<nlohmann/json.hpp>`. These are the macros that change what the parser accepts or how it reports errors. The others either do not apply ([`JSON_DIAGNOSTICS`](https://json.nlohmann.me/api/macros/json_diagnostics/index.md) needs parent pointers in `basic_json` values) or only affect the values `materialize()` produces, through `BasicJsonType`.
@@ -129,7 +135,7 @@ true
- **Chained access is safe.** [`operator[]`](https://json.nlohmann.me/api/basic_json_view/operator%5B%5D/index.md) with a missing key, an index out of range, or an unresolvable JSON pointer returns a [discarded](https://json.nlohmann.me/api/basic_json_view/is_discarded/index.md) view, and `operator[]` on a discarded view returns a discarded view without throwing: `v["a"]["b"][0]` can be tested once at the end. Type errors on values that exist (a key on an array, an index on an object) still throw, and [`at`](https://json.nlohmann.me/api/basic_json_view/at/index.md) throws for every missing value.
- **Duplicate keys: lookups find the first member, `parse()` keeps the last.** See [Duplicate keys](#duplicate-keys) below.
- **No [`JSON_DIAGNOSTICS`](https://json.nlohmann.me/api/macros/json_diagnostics/index.md) path.** Exceptions thrown by `basic_json_view`'s own element access and lookup functions never carry the JSON Pointer path `JSON_DIAGNOSTICS` would otherwise add: the view has no `basic_json` value to point at, so the exception is created without one, regardless of how `BasicJsonType` was built.
- **Ordering comparisons are not provided** by `basic_json_view` -- there is no `operator<`. [`operator==`](https://json.nlohmann.me/api/basic_json_view/operator_eq/index.md) and [`operator!=`](https://json.nlohmann.me/api/basic_json_view/operator_ne/index.md) are provided, though: two views, or a view and a `BasicJsonType` value, compare equal exactly when [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) or [`parse()`](https://json.nlohmann.me/api/basic_json/parse/index.md) would produce equal values for them, without ever building a tree to do it. For ordering, too, [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) is the way to get a value you can compare.
- **Ordering comparisons are not provided** by `basic_json_view` — there is no `operator<`. [`operator==`](https://json.nlohmann.me/api/basic_json_view/operator_eq/index.md) and [`operator!=`](https://json.nlohmann.me/api/basic_json_view/operator_ne/index.md) are provided, though: two views, or a view and a `BasicJsonType` value, compare equal exactly when [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) or [`parse()`](https://json.nlohmann.me/api/basic_json/parse/index.md) would produce equal values for them, without ever building a tree to do it. For ordering, too, [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md) is the way to get a value you can compare.
## Duplicate keys
@@ -167,10 +173,10 @@ If you cannot trust the source text to have unique keys, check [`size()`](https:
Two conversions never copy at all:
- [`get_string()`](https://json.nlohmann.me/api/basic_json_view/get_string/index.md) (equivalently, `get<string_view_t>()`) returns a string as a `string_view_t` pointing into the document's [`source()`](https://json.nlohmann.me/api/basic_json_document/source/index.md) text -- or, for a string that contains escape sequences, into the document's own buffer of decoded strings -- instead of allocating a new `std::string`.
- [`get_string()`](https://json.nlohmann.me/api/basic_json_view/get_string/index.md) (equivalently, `get<string_view_t>()`) returns a string as a `string_view_t` pointing into the document's [`source()`](https://json.nlohmann.me/api/basic_json_document/source/index.md) text — or, for a string that contains escape sequences, into the document's own buffer of decoded strings — instead of allocating a new `std::string`.
- [`number_token()`](https://json.nlohmann.me/api/basic_json_view/number_token/index.md) returns a number exactly as it was written in the source, e.g. `"1.50"`, `"1E2"`, or an integer with more digits than any number type holds, instead of rounding it into a `double`/`int64_t` the way `get<T>()` (and [`basic_json::parse()`](https://json.nlohmann.me/api/basic_json/parse/index.md)) would.
Both results are only valid as long as the view -- and, for a string with no escapes, the borrowed source text -- is.
Both results are only valid as long as the view — and, for a string with no escapes, the borrowed source text — is.
## Writing generic code
@@ -230,26 +236,64 @@ Two things differ when a `from_json()` reads the view:
## Writing a view back
[`dump()`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) serializes a view directly from the flat index, without ever building a `basic_json` value. An object's members are written in document order, not sorted by key, and *every* occurrence of a repeated key is written, not only the last one -- the same two ways [iteration](#what-is-different) already differs from a [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md)d value, see [Duplicate keys](#duplicate-keys). `materialize().dump()` gives a different result in both respects for a `json_view`.
[`dump()`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) serializes a view directly from the flat index, without ever building a `basic_json` value. An object's members are written in document order, not sorted by key, and *every* occurrence of a repeated key is written, not only the last one — the same two ways [iteration](#what-is-different) already differs from a [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md)d value, see [Duplicate keys](#duplicate-keys). `materialize().dump()` gives a different result in both respects for a `json_view`.
By default, numbers are written the way [`basic_json::dump()`](https://json.nlohmann.me/api/basic_json/dump/index.md) would. [`number_format::source`](https://json.nlohmann.me/api/basic_json_view/number_format/index.md) instead copies every number exactly as it was written in the source text -- a price like `19.90`, a long order or account ID with more digits than any number type holds, or a high-precision coordinate -- something `basic_json` cannot do at all, since parsing already reduces a number to its parsed `double`/`int64_t` value.
By default, numbers are written the way [`basic_json::dump()`](https://json.nlohmann.me/api/basic_json/dump/index.md) would. [`number_format::source`](https://json.nlohmann.me/api/basic_json_view/number_format/index.md) instead copies every number exactly as it was written in the source text — a price like `19.90`, a long order or account ID with more digits than any number type holds, or a high-precision coordinate — something `basic_json` cannot do at all, since parsing already reduces a number to its parsed `double`/`int64_t` value.
[`operator<<`](https://json.nlohmann.me/api/basic_json_view/operator_ltlt/index.md) writes a view to a stream the way `basic_json`'s does, using the stream's `width`/`fill` for indentation.
## Editing a document
Everything above is read-only: a `json_document`/`json_view` lets you look at a parsed text without copying it, but not change it. [`basic_json_document<BasicJsonType, view_mode::editable>`](https://json.nlohmann.me/api/basic_json_document/index.md) -- more conveniently spelled [`json_editable_document`](https://json.nlohmann.me/api/json_editable_document/index.md) or [`ordered_json_editable_document`](https://json.nlohmann.me/api/ordered_json_editable_document/index.md) -- also lets you [`set`](https://json.nlohmann.me/api/basic_json_document/set/index.md) a value, [`push_back`](https://json.nlohmann.me/api/basic_json_document/push_back/index.md) onto or [`insert`](https://json.nlohmann.me/api/basic_json_document/insert/index.md) into an array, and [`erase`](https://json.nlohmann.me/api/basic_json_document/erase/index.md) an object member or an array element, still without ever building a `basic_json` tree for parts you do not touch.
Everything above is read-only: a `json_document`/`json_view` lets you look at a parsed text without copying it, but not change it. [`basic_json_document<BasicJsonType, view_mode::editable>`](https://json.nlohmann.me/api/basic_json_document/index.md) — more conveniently spelled [`json_editable_document`](https://json.nlohmann.me/api/json_editable_document/index.md) or [`ordered_json_editable_document`](https://json.nlohmann.me/api/ordered_json_editable_document/index.md) — also lets you [`set`](https://json.nlohmann.me/api/basic_json_document/set/index.md) a value, [`push_back`](https://json.nlohmann.me/api/basic_json_document/push_back/index.md) onto or [`insert`](https://json.nlohmann.me/api/basic_json_document/insert/index.md) into an array, and [`erase`](https://json.nlohmann.me/api/basic_json_document/erase/index.md) an object member or an array element, still without ever building a `basic_json` tree for parts you do not touch.
The [mode](https://json.nlohmann.me/api/view_mode/index.md) defaults to `view_mode::read_only`, so `json_document`/`ordered_json_document` are unaffected -- they carry none of the bookkeeping edits need, and calling `set`/`push_back`/`insert`/`erase` on one is a compile error, not a runtime one.
The [mode](https://json.nlohmann.me/api/view_mode/index.md) defaults to `view_mode::read_only`, so `json_document`/`ordered_json_document` are unaffected — they carry none of the bookkeeping edits need, and calling `set`/`push_back`/`insert`/`erase` on one is a compile error, not a runtime one.
### Why: editing without reformatting
The `19.90` price from [above](#writing-a-view-back) is exactly the kind of value that makes editing a `json` or `ordered_json` value in place lossy. Say you parse a configuration file, patch one field, and write it back:
- **`json`** re-sorts every key on the way in (`object_t` is a `std::map`) and rewrites every number to its shortest round-trip form on the way out -- a one-field patch turns into a diff that reorders the whole file and rewrites `19.90` to `19.9`.
- **`json`** re-sorts every key on the way in (`object_t` is a `std::map`) and rewrites every number to its shortest round-trip form on the way out — a one-field patch turns into a diff that reorders the whole file and rewrites `19.90` to `19.9`.
- **`ordered_json`** keeps the key order, but still rewrites every number the same way: parsing has already reduced it to a `double`/`int64_t`, and there is no way back to how it was spelled in the source text.
An editable document keeps both. [`dump()`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) of an edited document writes members in document order -- a member [`set`](https://json.nlohmann.me/api/basic_json_document/set/index.md) added goes at the end, exactly where it was inserted, and an [`erase`](https://json.nlohmann.me/api/basic_json_document/erase/index.md)d member simply leaves a gap: everything around it keeps its place -- and [`number_format::source`](https://json.nlohmann.me/api/basic_json_view/number_format/index.md) keeps the exact spelling of every number an edit did not itself touch; a number an edit *did* touch is written the way [`BasicJsonType::dump()`](https://json.nlohmann.me/api/basic_json/dump/index.md) would write it, since there is no source spelling for a brand new value.
The patch of the example below (set `"host"`, add `"retries"`) written back by each of them:
`json`
keys sorted, numbers rewritten
```
{"host":"db2",
"name":"cache",
"port":6379,
"price":19.9,
"retries":3}
```
`ordered_json`
key order kept, numbers rewritten
```
{"name":"cache",
"host":"db2",
"port":6379,
"price":19.9,
"retries":3}
```
`json_editable_document`
order and spelling kept
```
{"name":"cache",
"host":"db2",
"port":6379,
"price":19.90,
"retries":3}
```
An editable document keeps both. [`dump()`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) of an edited document writes members in document order — a member [`set`](https://json.nlohmann.me/api/basic_json_document/set/index.md) added goes at the end, exactly where it was inserted, and an [`erase`](https://json.nlohmann.me/api/basic_json_document/erase/index.md)d member simply leaves a gap: everything around it keeps its place — and [`number_format::source`](https://json.nlohmann.me/api/basic_json_view/number_format/index.md) keeps the exact spelling of every number an edit did not itself touch; a number an edit *did* touch is written the way [`BasicJsonType::dump()`](https://json.nlohmann.me/api/basic_json/dump/index.md) would write it, since there is no source spelling for a brand new value.
Example: patch a configuration, keeping member order and number spellings
@@ -297,15 +341,29 @@ Output:
### What stays valid, and what an edit costs
The source text itself is **never written**, and the parsed index never moves -- a value keeps the node it was parsed into for as long as it is not itself replaced. So every [view](https://json.nlohmann.me/api/basic_json_view/index.md) taken before an edit, including a previously obtained [`root()`](https://json.nlohmann.me/api/basic_json_document/root/index.md), stays valid and, if it still refers to the edited value, sees the edit; a view of a value a later edit drops or replaces just keeps showing what it last held. New values go to storage the document allocates and owns on demand. The one thing an edit does invalidate is the **iterators** taken over an edited array or object: the first time one of its elements is set, appended to, inserted into, or erased, its elements move from the parsed, fixed layout to a growable block of links so that [`push_back`](https://json.nlohmann.me/api/basic_json_document/push_back/index.md) can later grow it in amortized constant time -- existing elements are not touched, but an iterator that was walking the old layout no longer matches. A string obtained with [`get_string()`](https://json.nlohmann.me/api/basic_json_view/get_string/index.md) is unaffected either way and stays valid across further edits. See [`basic_json_document`'s Edits](https://json.nlohmann.me/api/basic_json_document/#edits) for the details, and [`set`'s Exception safety](https://json.nlohmann.me/api/basic_json_document/set/#exception-safety) for what an edit guarantees if it throws (the *basic* guarantee, not the strong one `dump()` and the read-only functions provide). How edits are kept in the index is described in the [architecture overview](https://json.nlohmann.me/home/architecture/#node-index-of-json-views).
The source text itself is **never written**, and the parsed index never moves — a value keeps the node it was parsed into for as long as it is not itself replaced. So every [view](https://json.nlohmann.me/api/basic_json_view/index.md) taken before an edit, including a previously obtained [`root()`](https://json.nlohmann.me/api/basic_json_document/root/index.md), stays valid and, if it still refers to the edited value, sees the edit; a view of a value a later edit drops or replaces just keeps showing what it last held. New values go to storage the document allocates and owns on demand. The one thing an edit does invalidate is the **iterators** taken over an edited array or object: the first time an element is added to it, inserted into it, erased from it, or replaced by a value that spans a different number of nodes, its elements move from the parsed, fixed layout to a growable block of links so that [`push_back`](https://json.nlohmann.me/api/basic_json_document/push_back/index.md) can later grow it in amortized constant time — existing elements are not touched, but an iterator that was walking the old layout no longer matches. A string obtained with [`get_string()`](https://json.nlohmann.me/api/basic_json_view/get_string/index.md) is unaffected either way and stays valid across further edits. See [`basic_json_document`'s Edits](https://json.nlohmann.me/api/basic_json_document/#edits) for the details, and [`set`'s Exception safety](https://json.nlohmann.me/api/basic_json_document/set/#exception-safety) for what an edit guarantees if it throws (the *basic* guarantee, not the strong one `dump()` and the read-only functions provide). How edits are kept in the index is described in the [architecture overview](https://json.nlohmann.me/home/architecture/#node-index-of-json-views).
Step through the edits of a small document (the node fields follow the [architecture overview](https://json.nlohmann.me/home/architecture/#node-index-of-json-views)):
Step 1 of 5. After parsing: nine nodes in the parsed index, no owned storage yet.
Step 2 of 5. `doc.set(doc.root(), "port", 6380)` overwrites the node of the integer in place; no member is added or removed, so the object keeps its parsed layout.
Step 3 of 5. `doc.erase(doc.root()["tags"], 0)`: the array is edited for the first time, so its elements move to a block of links in owned storage. The node of `"eu"` stays in the index, but nothing refers to it any more; iterators over the array taken before are invalid.
Step 4 of 5. `doc.push_back(doc.root()["tags"], "new")`: the new string gets its own node, its text goes to the buffer of edited text, and a link to it is appended to the block of the array.
Step 5 of 5. `doc.set(doc.root(), "retries", 3)`: a new member, so the object moves to a block too: copies of its keys, links to the values it already had, then the new key and a link to the new integer.
The node index and the owned storage of the document after each of five edits. Use *Back*, *Next*, or the numbered buttons to switch between the steps; without JavaScript all steps are shown one below the other.
## Images
[`save()`](https://json.nlohmann.me/api/basic_json_document/save/index.md) writes a document as an *image*: a byte buffer that [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) reads back into a document without parsing -- no lexing, no building the node index, nothing but copying the nodes and pointing the text and the decoded strings at the image. Where [`parse_copy()`](https://json.nlohmann.me/api/basic_json_document/parse_copy/index.md) still has to scan the whole input, [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) turns that scan into a copy of the node index alone.
[`save()`](https://json.nlohmann.me/api/basic_json_document/save/index.md) writes a document as an *image*: a byte buffer that [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) reads back into a document without parsing — no lexing, no building the node index, nothing but copying the nodes and pointing the text and the decoded strings at the image. Where [`parse_copy()`](https://json.nlohmann.me/api/basic_json_document/parse_copy/index.md) still has to scan the whole input, [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) turns that scan into a copy of the node index alone.
**Why.** A document that is parsed once and then read many times -- a configuration loaded at startup, a template rendered on every request, a large reference dataset a worker process needs in memory -- pays for parsing once but can amortize [`save()`](https://json.nlohmann.me/api/basic_json_document/save/index.md)'s cost across every later load. That makes images useful for a cache: save a document the first time it is parsed (to a file, a shared-memory segment, an in-process cache), and [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) it on every later use instead of parsing the source text again. They are just as useful for handing a parsed document to another process (or a forked worker) running the same build of the library, since [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) turns the transfer into a copy of the node index plus pointers into the received bytes, not a re-parse.
**Why.** A document that is parsed once and then read many times — a configuration loaded at startup, a template rendered on every request, a large reference dataset a worker process needs in memory — pays for parsing once but can amortize [`save()`](https://json.nlohmann.me/api/basic_json_document/save/index.md)'s cost across every later load. That makes images useful for a cache: save a document the first time it is parsed (to a file, a shared-memory segment, an in-process cache), and [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) it on every later use instead of parsing the source text again. They are just as useful for handing a parsed document to another process (or a forked worker) running the same build of the library, since [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) turns the transfer into a copy of the node index plus pointers into the received bytes, not a re-parse.
**Choosing a check.** [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) takes an [`image_check`](https://json.nlohmann.me/api/basic_json_document/load/#image_check) that trades validation against speed: `image_check::full` (the default) checks everything the parser itself guarantees, so a checked image is exactly as safe to read and serialize as a freshly parsed document -- the right choice whenever the image did not come straight from this process's own [`save()`](https://json.nlohmann.me/api/basic_json_document/save/index.md), such as a file or a network peer. `image_check::bounds` only checks structure and bounds -- cheaper, since it skips scanning the text and the decoded strings -- and fits a cache the process trusts, one it wrote and reads back itself. `image_check::none` skips validation entirely, for an image trusted as much as the process's own memory. See [`load()`'s Notes](https://json.nlohmann.me/api/basic_json_document/load/#notes) for exactly what each level does and does not guarantee.
**Choosing a check.** [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) takes an [`image_check`](https://json.nlohmann.me/api/basic_json_document/load/#image_check) that trades validation against speed: `image_check::full` (the default) checks everything the parser itself guarantees, so a checked image is exactly as safe to read and serialize as a freshly parsed document — the right choice whenever the image did not come straight from this process's own [`save()`](https://json.nlohmann.me/api/basic_json_document/save/index.md), such as a file or a network peer. `image_check::bounds` only checks structure and bounds — cheaper, since it skips scanning the text and the decoded strings — and fits a cache the process trusts, one it wrote and reads back itself. `image_check::none` skips validation entirely, for an image trusted as much as the process's own memory. See [`load()`'s Notes](https://json.nlohmann.me/api/basic_json_document/load/#notes) for exactly what each level does and does not guarantee.
Example: cache a parsed configuration as an image
@@ -352,7 +410,7 @@ true
Experimental
The image format is versioned but not yet stable, and may change in an incompatible way before it is declared stable. It is little-endian only (on big-endian targets, `save()` and `load()` do not exist), and tied to the library build that wrote it -- use it to cache a document or to hand one to another process running the *same* build, not as a long-term storage format; keep the original JSON text if a saved document needs to be readable by a future library version.
The image format is versioned but not yet stable, and may change in an incompatible way before it is declared stable. It is little-endian only (on big-endian targets, `save()` and `load()` do not exist), and tied to the library build that wrote it — use it to cache a document or to hand one to another process running the *same* build, not as a long-term storage format; keep the original JSON text if a saved document needs to be readable by a future library version.
The idea of a document you can read without parsing comes from zero-copy formats such as [FlatBuffers](https://github.com/google/flatbuffers) and [YaFF](https://github.com/yandex/yaff); the check [`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md) runs follows the idea of FlatBuffers' Verifier. No code is taken from either.
@@ -363,8 +421,21 @@ The idea of a document you can read without parsing comes from zero-copy formats
| **Ownership** | owns every value | owns nothing; you decide what to keep, in your handler | borrows or owns the *text*; the index is always owned by the document | same as `json_document`; edits go to storage the document owns |
| **Mutability** | freely mutable | not applicable (a one-shot event stream) | read-only | [`set`](https://json.nlohmann.me/api/basic_json_document/set/index.md)/[`push_back`](https://json.nlohmann.me/api/basic_json_document/push_back/index.md)/[`insert`](https://json.nlohmann.me/api/basic_json_document/insert/index.md)/[`erase`](https://json.nlohmann.me/api/basic_json_document/erase/index.md) edit in place; the source text is never rewritten |
| **What you get** | a full tree you can read, write, and keep as long as you like | a sequence of callbacks; whatever your handler builds from them | a flat index plus, on demand, [`materialize()`](https://json.nlohmann.me/api/basic_json_view/materialize/index.md)d `json`/`ordered_json` values for the parts you actually use | the same, plus [`dump()`](https://json.nlohmann.me/api/basic_json_view/dump/index.md) of an edited document that keeps the member order and, with [`number_format::source`](https://json.nlohmann.me/api/basic_json_view/number_format/index.md), the spelling of every untouched number |
| **Typical use** | general-purpose JSON handling: config, request/response bodies you build or modify, anything you hold onto | validating or projecting a text into your own data structure without ever holding the whole thing as JSON | large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives | a document you read, patch a few fields of, and write back -- a configuration file, for instance -- where the rest of it should come back exactly as it was |
| **Caching/reload** | not applicable -- re-parse, or roll your own serialization | not applicable | [`save()`](https://json.nlohmann.me/api/basic_json_document/save/index.md)/[`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md): cache the parsed index as an image and reload it without parsing | same, saving the document's current -- possibly edited -- state |
| **Typical use** | general-purpose JSON handling: config, request/response bodies you build or modify, anything you hold onto | validating or projecting a text into your own data structure without ever holding the whole thing as JSON | large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives | a document you read, patch a few fields of, and write back — a configuration file, for instance — where the rest of it should come back exactly as it was |
| **Caching/reload** | not applicable — re-parse, or roll your own serialization | not applicable | [`save()`](https://json.nlohmann.me/api/basic_json_document/save/index.md)/[`load()`](https://json.nlohmann.me/api/basic_json_document/load/index.md): cache the parsed index as an image and reload it without parsing | same, saving the document's current — possibly edited — state |
Each column is one way to work with JSON in this library, and each row compares one property of them.
## See also
- [`basic_json_document`](https://json.nlohmann.me/api/basic_json_document/index.md) - the document that owns the parsed index (parse, access, images, edits)
- [`basic_json_view`](https://json.nlohmann.me/api/basic_json_view/index.md) - the non-owning handle to a value in a document
- [`json_document`](https://json.nlohmann.me/api/json_document/index.md) - and [`ordered_json_document`](https://json.nlohmann.me/api/ordered_json_document/index.md) - the read-only documents
- [`json_editable_document`](https://json.nlohmann.me/api/json_editable_document/index.md) - and [`ordered_json_editable_document`](https://json.nlohmann.me/api/ordered_json_editable_document/index.md) - the documents that can be edited in place
- [Architecture](https://json.nlohmann.me/home/architecture/#node-index-of-json-views) - the layout of the node index
- [SAX interface](https://json.nlohmann.me/features/parsing/sax_interface/index.md) - event-based parsing without building a tree
- [Performance](https://json.nlohmann.me/features/performance/index.md) - benchmarks and tuning of parsing and serialization
- [Large, Streamed, and Partial Input](https://json.nlohmann.me/features/parsing/large_and_partial_input/index.md) - reading only the part of a text you need
## Version history