mirror of
https://github.com/nlohmann/json.git
synced 2026-10-03 05:00:30 +00:00
Document element access, lookup, and iteration of json_view
- API pages for operator[], at, front, back, find, contains, count, begin, end, cbegin, cend, items, and type_name of basic_json_view, linked both ways with the basic_json pages - the feature page and size() describe document order and duplicate keys - the examples show when the view helps: reading a few fields of a large text, probing optional members, and members in source order Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_view = nlohmann::json_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// required fields of a service configuration -- at() reports a missing
|
||||
// or wrong-typed field with the very same exception basic_json::at()
|
||||
// would throw for the equivalent nlohmann::json value, so error
|
||||
// handling written against basic_json::at() keeps working unchanged
|
||||
json_document config = json_document::parse(R"({"name": "cache", "port": "6379"})");
|
||||
const json_view service = config.root();
|
||||
|
||||
std::cout << service.at("port").materialize().dump() << '\n';
|
||||
|
||||
try
|
||||
{
|
||||
// "port" is a string, not an array
|
||||
static_cast<void>(service.at("port").at(0));
|
||||
}
|
||||
catch (const nlohmann::json::type_error& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
static_cast<void>(service.at("timeout"));
|
||||
}
|
||||
catch (const nlohmann::json::out_of_range& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
"6379"
|
||||
[json.exception.type_error.304] cannot use at() with string
|
||||
[json.exception.out_of_range.403] key 'timeout' not found
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
// the same build log; back() reads only the final status. It is linear
|
||||
// in the number of events (unlike front(), which is constant), but
|
||||
// still far less work than materializing the whole array
|
||||
json_document log = json_document::parse(R"(["queued", "started", "compiling", "linking", "done"])");
|
||||
std::cout << log.root().back().materialize().dump() << '\n';
|
||||
|
||||
// an empty log -- back() throws instead of the undefined behavior
|
||||
// basic_json::back() has for an empty array
|
||||
json_document empty_log = json_document::parse("[]");
|
||||
try
|
||||
{
|
||||
static_cast<void>(empty_log.root().back());
|
||||
}
|
||||
catch (const nlohmann::json::invalid_iterator& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
"done"
|
||||
[json.exception.invalid_iterator.214] cannot get value
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a log record: the fields matter in the order they were written, e.g.
|
||||
// to reproduce the record as it was logged. A nlohmann::json object
|
||||
// sorts its keys, so materializing and iterating it would instead print
|
||||
// them alphabetically ("level", "message", "time")
|
||||
json_document record = json_document::parse(R"({"time": "10:00:01", "level": "info", "message": "started"})");
|
||||
|
||||
for (auto it = record.root().begin(); it != record.root().end(); ++it)
|
||||
{
|
||||
std::cout << it.key() << '=' << it->materialize().dump() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
time="10:00:01"
|
||||
level="info"
|
||||
message="started"
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_view = nlohmann::json_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// sum many measurements with std::accumulate; cbegin()/cend() (identical
|
||||
// to begin()/end() here -- the view is always read-only) let the view be
|
||||
// used with standard algorithms without ever materializing the whole
|
||||
// array into a nlohmann::json value
|
||||
json_document measurements = json_document::parse("[3, 1, 4, 1, 5, 9, 2, 6]");
|
||||
const auto values = measurements.root();
|
||||
|
||||
const int sum = std::accumulate(values.cbegin(), values.cend(), 0,
|
||||
[](int total, const json_view & v)
|
||||
{
|
||||
return total + v.materialize().get<int>();
|
||||
});
|
||||
|
||||
std::cout << sum << '\n';
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
31
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_view = nlohmann::json_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// check that every element of a (possibly large) batch is an object,
|
||||
// before materializing any of them -- cbegin()/cend() (identical to
|
||||
// begin()/end() here) work as the range for std::all_of like they would
|
||||
// for any standard container
|
||||
json_document batch = json_document::parse(R"([{"id": 1}, {"id": 2}, {"id": 3}])");
|
||||
const auto records = batch.root();
|
||||
|
||||
const bool all_objects = std::all_of(records.cbegin(), records.cend(),
|
||||
[](const json_view & v)
|
||||
{
|
||||
return v.is_object();
|
||||
});
|
||||
|
||||
std::cout << std::boolalpha << all_objects << '\n';
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
true
|
||||
@@ -0,0 +1,30 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
// count how many of many incoming records carry an optional "retry_of"
|
||||
// field -- contains() only walks the flat index, so scanning a large
|
||||
// batch like this never builds a single nlohmann::json value
|
||||
json_document batch = json_document::parse(R"(
|
||||
[
|
||||
{"id": 1},
|
||||
{"id": 2, "retry_of": 1},
|
||||
{"id": 3},
|
||||
{"id": 4, "retry_of": 3}
|
||||
]
|
||||
)");
|
||||
|
||||
const auto records = batch.root();
|
||||
std::size_t retries = 0;
|
||||
for (std::size_t i = 0; i < records.size(); ++i)
|
||||
{
|
||||
if (records[i].contains("retry_of"))
|
||||
{
|
||||
++retries;
|
||||
}
|
||||
}
|
||||
std::cout << retries << " of " << records.size() << " records are retries\n";
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
2 of 4 records are retries
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
// validate that every transaction of a batch carries a mandatory
|
||||
// "amount" field before materializing any of them into a nlohmann::json
|
||||
// value -- count() returns 0 or 1 for an object
|
||||
json_document batch = json_document::parse(R"(
|
||||
[
|
||||
{"id": 1, "amount": 9.99},
|
||||
{"id": 2}
|
||||
]
|
||||
)");
|
||||
|
||||
const auto transactions = batch.root();
|
||||
for (std::size_t i = 0; i < transactions.size(); ++i)
|
||||
{
|
||||
const auto transaction = transactions[i];
|
||||
if (transaction.count("amount") == 0)
|
||||
{
|
||||
std::cout << "transaction " << i << " is missing \"amount\"\n";
|
||||
continue;
|
||||
}
|
||||
std::cout << "transaction " << i << ": " << transaction["amount"].materialize().dump() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
transaction 0: 9.99
|
||||
transaction 1 is missing "amount"
|
||||
@@ -0,0 +1,27 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
// scan a (possibly large) array of readings for the first one over a
|
||||
// threshold; the loop stops at end() as soon as one is found, and only
|
||||
// the matching reading is ever materialized
|
||||
json_document readings = json_document::parse("[12, 18, 25, 31, 9]");
|
||||
const auto values = readings.root();
|
||||
|
||||
auto it = values.begin();
|
||||
for (; it != values.end(); ++it)
|
||||
{
|
||||
if (it->materialize().get<int>() > 20)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (it != values.end())
|
||||
{
|
||||
std::cout << "first reading over 20: " << it->materialize().dump() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
first reading over 20: 25
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a batch of incoming events; only some carry a "user_id" -- find()
|
||||
// locates it without throwing for the events that turn out to not be
|
||||
// objects, and without materializing an event that does not match
|
||||
json_document batch = json_document::parse(R"(
|
||||
[
|
||||
{"type": "click", "user_id": 42},
|
||||
{"type": "ping"},
|
||||
{"type": "click", "user_id": 7}
|
||||
]
|
||||
)");
|
||||
|
||||
const auto events = batch.root();
|
||||
for (std::size_t i = 0; i < events.size(); ++i)
|
||||
{
|
||||
const auto event = events[i];
|
||||
const auto it = event.find("user_id");
|
||||
if (it != event.end())
|
||||
{
|
||||
std::cout << "user " << it->materialize().dump() << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
user 42
|
||||
user 7
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
// the build log of a running job; front() reads just the earliest event
|
||||
// without materializing the (possibly long) rest of the log
|
||||
json_document log = json_document::parse(R"(["queued", "started", "compiling", "linking", "done"])");
|
||||
std::cout << log.root().front().materialize().dump() << '\n';
|
||||
|
||||
// an empty log -- front() throws instead of the undefined behavior
|
||||
// basic_json::front() has for an empty array
|
||||
json_document empty_log = json_document::parse("[]");
|
||||
try
|
||||
{
|
||||
static_cast<void>(empty_log.root().front());
|
||||
}
|
||||
catch (const nlohmann::json::invalid_iterator& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
"queued"
|
||||
[json.exception.invalid_iterator.214] cannot get value
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a settings object whose source text records every update to a key as
|
||||
// a duplicate member. items() visits all of them, in document order, so
|
||||
// the update history is visible; operator[] only ever sees the first
|
||||
// one, and materialize() -- like basic_json::parse() -- keeps the last
|
||||
json_document updates = json_document::parse(R"({"retries": 1, "timeout": 30, "retries": 5})");
|
||||
const auto settings = updates.root();
|
||||
|
||||
for (const auto& item : settings.items())
|
||||
{
|
||||
std::cout << item.key() << '=' << item.value().materialize().dump() << '\n';
|
||||
}
|
||||
|
||||
std::cout << "first \"retries\" seen by operator[]: " << settings["retries"].materialize().dump() << '\n';
|
||||
std::cout << "last \"retries\" kept by materialize(): " << settings.materialize()["retries"].dump() << '\n';
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
retries=1
|
||||
timeout=30
|
||||
retries=5
|
||||
first "retries" seen by operator[]: 1
|
||||
last "retries" kept by materialize(): 5
|
||||
@@ -0,0 +1,42 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
// two user records from a large API response; only the fields that are
|
||||
// actually read are ever touched, and no nlohmann::json tree is built
|
||||
// for the batch
|
||||
json_document batch = json_document::parse(R"(
|
||||
[
|
||||
{"name": "Alice", "email": "alice@example.com", "tags": ["admin", "ops"]},
|
||||
{"name": "Bob", "tags": []}
|
||||
]
|
||||
)");
|
||||
|
||||
const auto users = batch.root();
|
||||
for (std::size_t i = 0; i < users.size(); ++i)
|
||||
{
|
||||
const auto user = users[i];
|
||||
std::cout << user["name"].materialize().dump();
|
||||
|
||||
// operator[] on a missing object key gives a discarded view -- test
|
||||
// it with a plain "if". The const overload of json::operator[]
|
||||
// would instead be undefined behavior (guarded by an assertion) for
|
||||
// a missing key
|
||||
if (const auto email = user["email"])
|
||||
{
|
||||
std::cout << " <" << email.materialize().dump() << ">";
|
||||
}
|
||||
|
||||
// the same holds for an array index past the end: a discarded view,
|
||||
// not undefined behavior
|
||||
if (const auto first_tag = user["tags"][0])
|
||||
{
|
||||
std::cout << " #" << first_tag.materialize().dump();
|
||||
}
|
||||
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
"Alice" <"alice@example.com"> #"admin"
|
||||
"Bob"
|
||||
@@ -0,0 +1,27 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_view = nlohmann::json_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// report why some parsed messages were rejected, using only
|
||||
// type_name() -- no nlohmann::json value is built for the ones that
|
||||
// are wrong
|
||||
json_document good = json_document::parse(R"({"id": 1})");
|
||||
json_document bad = json_document::parse("[1, 2, 3]");
|
||||
json_document failed = json_document::parse("not json", /* allow_exceptions */ false);
|
||||
|
||||
for (const json_view v : { good.root(), bad.root(), failed.root() })
|
||||
{
|
||||
if (v.is_object())
|
||||
{
|
||||
std::cout << "ok\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "expected an object, got " << v.type_name() << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
ok
|
||||
expected an object, got array
|
||||
expected an object, got discarded
|
||||
Reference in New Issue
Block a user