| # <small>nlohmann::basic_json_document::</small>load | |
| ```cpp | |
| // (1) | |
| static basic_json_document load(const std::uint8_t* image, std::size_t size, | |
| const image_check check = image_check::full); | |
| // (2) | |
| static basic_json_document load(const std::vector<std::uint8_t>& image, | |
| const image_check check = image_check::full); | |
| // (3) | |
| static basic_json_document load(std::vector<std::uint8_t>&& image, | |
| const image_check check = image_check::full); | |
| ``` | |
| 1. Reads an image [`save()`](save.md) wrote, from a pointer and a byte count. The image is **borrowed**: `image` | |
| must stay alive and unchanged for as long as the returned document, and any view taken from it, is used. | |
| 2. Reads an image from a `#!cpp std::vector`. Also **borrowed** — equivalent to overload 1 called with | |
| `#!cpp image.data()` and `#!cpp image.size()`. | |
| 3. Reads an image, keeping the vector instead of copying it: `image` is moved into the document (no copy), which | |
| then owns it for as long as it needs the text and the decoded strings. [`owns_source()`](owns_source.md) is | |
| `#!cpp true` afterward. | |
| In every overload, the node index is copied into storage the document itself owns — so that it is properly aligned, | |
| and, for an [editable](index.md#edits) document, can be edited — while the text and the decoded strings stay in | |
| `image`. The hash indexes [large objects](../../features/json_view.md) use for lookup are rebuilt, exactly as after | |
| parsing. | |
| ## Parameters | |
| `image` (in) | |
| : the image [`save()`](save.md) wrote (overloads 1 and 2), or one to take ownership of (overload 3) | |
| `size` (in) | |
| : the number of bytes at `image` (overload 1) | |
| `check` (in) | |
| : how thoroughly to validate `image` before trusting it; see [`image_check`](#image_check) below (optional, | |
| `#!cpp image_check::full` by default) | |
| ## Return value | |
| The document read from the image. | |
| ## Exception safety | |
| Overloads 1 and 2 give the strong guarantee: `image` is only read, never written, so a thrown exception leaves the | |
| caller's buffer untouched. | |
| Overload 3 moves `image` into the document *before* validating it, so that a good image is kept without a copy. If | |
| loading then fails, the partially built document — and the vector now inside it — is discarded along with the | |
| exception, and `image` itself is left **empty**, not restored to what was passed in. Move a copy in instead, or | |
| validate with overload 2 first, if the original vector must survive a failed load. | |
| ## Exceptions | |
| Throws [`parse_error.116`](../../home/exceptions.md#jsonexceptionparse_error116) if `image` is not one | |
| `save()` could have written, or fails the requested `check`: | |
| | message | when | | |
| |------------------------|------------------------------------------------------------------------------------------------------| | |
| | `too short` | `image` is `#!cpp nullptr`, or `size` is smaller than the 64-byte header | | |
| | `unknown format` | the header's magic bytes or version do not match, or a reserved header field is not zero | | |
| | `sizes out of range` | the node count, text size, or decoded-string size the header describes does not fit `size`, or the `#!cpp '\0'` after the text or after the decoded strings is missing | | |
| | `the check failed` | `check` is not `#!cpp image_check::none`, and the image fails it — see [`image_check`](#image_check) | | |
| !!! failure "Example messages" | |
| ``` | |
| [json.exception.parse_error.116] parse error: invalid json_document image: too short | |
| ``` | |
| ``` | |
| [json.exception.parse_error.116] parse error: invalid json_document image: unknown format | |
| ``` | |
| ``` | |
| [json.exception.parse_error.116] parse error: invalid json_document image: sizes out of range | |
| ``` | |
| ``` | |
| [json.exception.parse_error.116] parse error: invalid json_document image: the check failed | |
| ``` | |
| ## Complexity | |
| Linear in the number of nodes, which are always copied into the document. `#!cpp image_check::none` does not read | |
| the text or the decoded strings. `#!cpp image_check::bounds` reads one byte of the text for each float token (its sign, | |
| to check the recorded digits against the token's length), and nothing else of the text or the decoded strings. | |
| With `#!cpp image_check::full`, linear in the size of the image (the nodes, the text, and the decoded strings), plus | |
| sorting the ranges of the strings and of the float tokens (at most one per node): the contents of each distinct range | |
| are checked once, however many nodes refer to it. | |
| ## Notes | |
| **Little-endian targets only.** On a big-endian target, `load()` does not exist, just like | |
| [`save()`](save.md#notes): the image format is little-endian, so a call fails to compile. | |
| ### `image_check` { #image_check } | |
| ```cpp | |
| using image_check = detail::view::image_check; | |
| enum class image_check | |
| { | |
| full, | |
| bounds, | |
| none | |
| }; | |
| ``` | |
| How thoroughly `load()` validates `image` before trusting it. | |
| `full` (the default) | |
| : Checks everything the parser itself guarantees: structure and bounds; that every string is valid UTF-8 (and, for a | |
| string still in the source text, that it contains no quote, backslash, or control character); and that every number | |
| token is well-formed and matches the value stored for it. Strings and float tokens may share a range only if the | |
| ranges are identical (as nodes that share a value do), and never overlap otherwise. Reading and serializing a | |
| checked image is safe and always produces valid JSON, exactly as for a parsed document. This is the right choice for | |
| an image from anything you do not fully control — a file, a cache shared with other processes, a peer on the | |
| network. | |
| `bounds` | |
| : Checks structure and bounds only: that every offset and count in the node index stays inside the image. So | |
| `load()`, [`root()`](root.md), element access, and [`materialize()`](../basic_json_view/materialize.md) can never | |
| read outside the image, even on a damaged one. It does *not* guarantee that the image describes valid JSON: a | |
| string that a `full` check would have rejected can make [`dump()`](../basic_json_view/dump.md) write invalid UTF-8 | |
| or invalid JSON, or throw [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316), and a number can | |
| read back with a value that does not match how it is spelled. An [editable document](../json_editable_document.md) | |
| does not take such a string over: [`set`](set.md), [`push_back`](push_back.md) and [`insert`](insert.md) throw | |
| `type_error.316` when they copy it from a view of the loaded document, and leave the editable document unchanged. | |
| `bounds` skips scanning the text and the decoded strings, so it fits a cache your own process just wrote and reads | |
| straight back, where damage would mean a bug or a hardware fault rather than adversarial input. | |
| `none` | |
| : Checks nothing. Reading a damaged image is undefined behavior, so use it only for an image you trust as much as | |
| your own memory. | |
| **Lifetime.** Overloads 1 and 2 borrow `image`: it must stay alive and byte-for-byte unchanged for as long as the | |
| returned document, and any [view](../basic_json_view/index.md) taken from it, is used — exactly like a document | |
| [`parse()`](parse.md) borrowed its input for. Overload 3 avoids this by keeping the vector itself; see | |
| [`owns_source`](owns_source.md). | |
| !!! warning "Experimental" | |
| The image format is versioned but not yet stable, and may change in an incompatible way before it is declared | |
| stable; `load()` already rejects an image written by a different format version with `parse_error.116` | |
| ("unknown format"). Use images to cache a document within one build of the library, or to hand one to another | |
| process running the *same* build on the *same* (little-endian) machine — not as a long-term storage format. | |
| ## Examples | |
| ??? example "Caching a document, ownership, and a rejected image" | |
| The example below saves a parsed document as an image, checks that `load()` reproduces the original | |
| [`dump()`](../basic_json_view/dump.md) without parsing, and shows the difference between | |
| `load(std::move(image))` (owned) and `load(image)` (borrowed). It then damages one byte of the image and shows | |
| `image_check::full` rejecting it with `parse_error.116`, while `image_check::bounds` — meant for a cache the | |
| process already trusts — still reads it without going out of bounds. | |
| ```cpp | |
| #include <cstdint> | |
| #include <iostream> | |
| #include <string> | |
| #include <vector> | |
| #include <nlohmann/json_view.hpp> | |
| using nlohmann::json; | |
| using nlohmann::json_document; | |
| using image_check = json_document::image_check; | |
| int main() | |
| { | |
| std::cout << std::boolalpha; | |
| // the image of a parsed document -- as if read back from a cache file or | |
| // received from another process running the same build of the library | |
| const std::string text = R"({"name": "cache", "note": "caf\u00e9", "replicas": ["db2", "db3"]})"; | |
| const json_document parsed = json_document::parse(text); | |
| const std::vector<std::uint8_t> image = parsed.save(); | |
| // (1)/(2) load() needs no parsing, yet dumps exactly what parsing did | |
| const json_document borrowed = json_document::load(image); | |
| std::cout << (borrowed.root().dump() == parsed.root().dump()) << '\n'; | |
| std::cout << borrowed.owns_source() << '\n'; // borrowed: still points into `image` | |
| // (3) load(std::move(image)) keeps the vector instead of copying it | |
| std::vector<std::uint8_t> to_move = image; | |
| const json_document owned = json_document::load(std::move(to_move)); | |
| std::cout << owned.owns_source() << '\n'; | |
| // a damaged image -- the last byte of the decoded string "note" holds | |
| // (an escape sequence, so it was unescaped into the document's own | |
| // buffer), flipped, as storage or transport corruption might do | |
| std::vector<std::uint8_t> damaged = image; | |
| damaged[damaged.size() - 2] = 0xFF; | |
| // image_check::full inspects strings and numbers, so it catches the damage | |
| try | |
| { | |
| static_cast<void>(json_document::load(damaged, image_check::full)); | |
| } | |
| catch (const json::parse_error& e) | |
| { | |
| std::cout << e.id << '\n'; | |
| } | |
| // image_check::bounds only checks structure and bounds, so a cache the | |
| // process already trusts loads without the extra scan -- reading a value | |
| // the damage did not touch is still safe | |
| const json_document trusted = json_document::load(damaged, image_check::bounds); | |
| std::cout << trusted.root()["name"].get<std::string>() << '\n'; | |
| } | |
| ``` | |
| Output: | |
| ```json | |
| true | |
| false | |
| true | |
| 116 | |
| cache | |
| ``` | |
| ## See also | |
| - [`save`](save.md) - write the document as an image | |
| - [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text | |
| - [`parse`](parse.md) - deserialize from JSON text instead of an image | |
| - [Images](../../features/json_view.md#images) - why and when to use images | |
| ## Version history | |
| - Added in version 3.13.0. |