1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
|
# Partial Read
At times it is not necessary to read the entire JSON document, but rather just a header or some of the initial fields.
This document describes a limited approach using the `partial_read` option, to quickly exit after parsing fields of interest.
For more advanced (and still performant) partial reading, Glaze provides compile-time and run-time [JMESPath support](./JMESPath.md).
# Partial reading with glz::opts
`partial_read` is a compile time flag in `glz::opts` that indicates only existing array and object elements should be read into, and once the memory has been read, parsing returns without iterating through the rest of the document.
> [!NOTE]
>
> Once any sub-object is read, the parsing will finish. This ensures high performance with short circuiting.
> A [wrapper](./wrappers.md) by the same name also exists.
Example: read only the first two elements into a `std::tuple`
```c++
std::string s = R"(["hello",88,"a string we don't care about"])";
std::tuple<std::string, int> obj{};
expect(!glz::read<glz::opts{.partial_read = true}>(obj, s));
expect(std::get<0>(obj) == "hello");
expect(std::get<1>(obj) == 88);
```
Example: read only the first two elements into a `std::vector`
```c++
std::string s = R"([1,2,3,4,5])";
std::vector<int> v(2);
expect(!glz::read<glz::opts{.partial_read = true}>(v, s));
expect(v.size() == 2);
expect(v[0] = 1);
expect(v[1] = 2);
```
Example: read only the allocated element in a `std::map`
```c++
std::string s = R"({"1":1,"2":2,"3":3})";
std::map<std::string, int> obj{{"2", 0}};
expect(!glz::read<glz::opts{.partial_read = true}>(obj, s));
expect(obj.size() == 1);
expect(obj.at("2") = 2);
```
Example: read only the fields present in a struct and then short circuit the parse
```c++
struct partial_struct
{
std::string string{};
int32_t integer{};
};
```
```c++
std::string s = R"({"integer":400,"string":"ha!",ignore})";
partial_struct obj{};
expect(!glz::read<glz::opts{.partial_read = true}>(obj, s));
expect(obj.string == "ha!");
expect(obj.integer == 400);
```
## Unit Test Examples
```c++
struct Header
{
std::string id{};
std::string type{};
};
struct HeaderFlipped
{
std::string type{};
std::string id{};
};
struct NestedPartialRead
{
std::string method{};
Header header{};
int number{};
};
suite partial_read_tests = [] {
using namespace ut;
static constexpr glz::opts partial_read{.partial_read = true};
"partial read"_test = [] {
Header h{};
std::string buf = R"({"id":"51e2affb","type":"message_type","unknown key":"value"})";
expect(!glz::read<partial_read>(h, buf));
expect(h.id == "51e2affb");
expect(h.type == "message_type");
};
"partial read 2"_test = [] {
Header h{};
// closing curly bracket is missing
std::string buf = R"({"id":"51e2affb","type":"message_type","unknown key":"value")";
expect(!glz::read<partial_read>(h, buf));
expect(h.id == "51e2affb");
expect(h.type == "message_type");
};
"partial read unknown key"_test = [] {
Header h{};
std::string buf = R"({"id":"51e2affb","unknown key":"value","type":"message_type"})";
expect(glz::read<partial_read>(h, buf) == glz::error_code::unknown_key);
expect(h.id == "51e2affb");
expect(h.type.empty());
};
"partial read unknown key 2"_test = [] {
Header h{};
std::string buf = R"({"id":"51e2affb","unknown key":"value","type":"message_type"})";
expect(!glz::read<glz::opts{.error_on_unknown_keys = false, .partial_read = true}>(h, buf));
expect(h.id == "51e2affb");
expect(h.type == "message_type");
};
"partial read don't read garbage"_test = [] {
Header h{};
std::string buf = R"({"id":"51e2affb","unknown key":"value","type":"message_type"garbage})";
expect(!glz::read<glz::opts{.error_on_unknown_keys = false, .partial_read = true}>(h, buf));
expect(h.id == "51e2affb");
expect(h.type == "message_type");
};
"partial read missing key"_test = [] {
Header h{};
std::string buf = R"({"id":"51e2affb","unknown key":"value"})";
expect(glz::read<glz::opts{.error_on_unknown_keys = false, .partial_read = true}>(h, buf) != glz::error_code::missing_key);
expect(h.id == "51e2affb");
expect(h.type.empty());
};
"partial read missing key 2"_test = [] {
Header h{};
std::string buf = R"({"id":"51e2affb","unknown key":"value"})";
expect(!glz::read<glz::opts{.error_on_unknown_keys = false, .partial_read = true}>(h, buf));
expect(h.id == "51e2affb");
expect(h.type.empty());
};
"partial read HeaderFlipped"_test = [] {
HeaderFlipped h{};
std::string buf = R"({"id":"51e2affb","type":"message_type","unknown key":"value"})";
expect(not glz::read<partial_read>(h, buf));
expect(h.id == "51e2affb");
expect(h.type == "message_type");
};
"partial read HeaderFlipped unknown key"_test = [] {
HeaderFlipped h{};
std::string buf = R"({"id":"51e2affb","unknown key":"value","type":"message_type"})";
expect(glz::read<partial_read>(h, buf) == glz::error_code::unknown_key);
expect(h.id == "51e2affb");
expect(h.type.empty());
};
"partial read unknown key 2 HeaderFlipped"_test = [] {
HeaderFlipped h{};
std::string buf = R"({"id":"51e2affb","unknown key":"value","type":"message_type","another_field":409845})";
expect(glz::read<glz::opts{.error_on_unknown_keys = false, .partial_read = true}>(h, buf) == glz::error_code::none);
expect(h.id == "51e2affb");
expect(h.type == "message_type");
};
};
suite nested_partial_read_tests = [] {
using namespace ut;
static constexpr glz::opts partial_read{.partial_read = true};
"nested object partial read"_test = [] {
NestedPartialRead n{};
std::string buf =
R"({"method":"m1","header":{"id":"51e2affb","type":"message_type","unknown key":"value"},"number":51})";
expect(not glz::read<partial_read>(n, buf));
expect(n.method == "m1");
expect(n.header.id == "51e2affb");
expect(n.header.type == "message_type");
expect(n.number == 0);
};
"nested object partial read, don't read garbage"_test = [] {
NestedPartialRead n{};
std::string buf =
R"({"method":"m1","header":{"id":"51e2affb","type":"message_type","unknown key":"value",garbage},"number":51})";
expect(not glz::read<partial_read>(n, buf));
expect(n.method == "m1");
expect(n.header.id == "51e2affb");
expect(n.header.type == "message_type");
expect(n.number == 0);
};
};
```
|