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
|
#ifndef CEPIS_JSON_HPP
#define CEPIS_JSON_HPP
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace cepis {
/// An immutable-by-convention JSON value, sufficient for this API's responses.
///
/// The library ships its own parser rather than depending on a general-purpose
/// one: the payloads are flat objects of strings and numbers, and a client
/// whose only dependency is libcurl is easier to drop into an existing build.
///
/// Objects preserve **insertion order** — the API documents a fixed field
/// order and round-tripping it through `dump()` keeps that visible. Lookup is
/// a linear scan, which is the faster choice at this size anyway.
///
/// The parser accepts UTF-8 input, decodes `\uXXXX` escapes (surrogate pairs
/// included) into UTF-8, and rejects trailing garbage after the top-level
/// value.
class Json {
public:
/// The six JSON types.
enum class Kind : std::uint8_t { Null, Bool, Number, String, Array, Object };
using Array = std::vector<Json>;
using Object = std::vector<std::pair<std::string, Json>>;
/// Constructs a null value.
Json() = default;
/// Constructs a value of the given kind, empty for the composite kinds.
static Json boolean(bool value);
static Json number(double value);
static Json string(std::string value);
static Json array(Array value = {});
static Json object(Object value = {});
/// Parses `text`, returning `std::nullopt` if it is not one well-formed
/// JSON value. Callers in this library turn that into `InvalidResponse`.
static std::optional<Json> parse(std::string_view text);
/// Serialises back to compact JSON. Non-ASCII is emitted as raw UTF-8.
[[nodiscard]] std::string dump() const;
[[nodiscard]] Kind kind() const noexcept { return kind_; }
[[nodiscard]] bool is_null() const noexcept { return kind_ == Kind::Null; }
[[nodiscard]] bool is_bool() const noexcept { return kind_ == Kind::Bool; }
[[nodiscard]] bool is_number() const noexcept { return kind_ == Kind::Number; }
[[nodiscard]] bool is_string() const noexcept { return kind_ == Kind::String; }
[[nodiscard]] bool is_array() const noexcept { return kind_ == Kind::Array; }
[[nodiscard]] bool is_object() const noexcept { return kind_ == Kind::Object; }
/// Typed accessors. Each returns a harmless default for a mismatched kind
/// rather than throwing: every field of this API is optional in practice,
/// and a wrong type on one field must not cost the whole response.
[[nodiscard]] bool as_bool(bool fallback = false) const noexcept;
[[nodiscard]] double as_number(double fallback = 0.0) const noexcept;
[[nodiscard]] const std::string& as_string() const noexcept;
[[nodiscard]] const Array& as_array() const noexcept;
[[nodiscard]] const Object& as_object() const noexcept;
/// Returns the member named `key`, or `nullptr` when this is not an object
/// or has no such member.
[[nodiscard]] const Json* find(std::string_view key) const noexcept;
/// Appends a member. Replaces the value if `key` is already present, so the
/// original position in the order is kept.
void set(std::string key, Json value);
/// Appends an element. Does nothing unless this is an array.
void push_back(Json value);
private:
Kind kind_ = Kind::Null;
bool bool_ = false;
double number_ = 0.0;
std::string string_;
Array array_;
Object object_;
};
} // namespace cepis
#endif // CEPIS_JSON_HPP
|