tgbot-cpp
Loading...
Searching...
No Matches
Json.h
Go to the documentation of this file.
1#pragma once
2
3#include <nlohmann/json.hpp>
4
5#include <cstddef>
6#include <memory>
7#include <optional>
8#include <stdexcept>
9#include <string>
10#include <utility>
11#include <variant>
12#include <vector>
13
15
16namespace TgBot::Json {
17
18template<typename T>
19void decode(const nlohmann::json& json, T& value);
20
21template<typename T>
22void decode(const nlohmann::json& json, std::shared_ptr<T>& value);
23
24template<typename T>
25void decode(const nlohmann::json& json, std::optional<T>& value);
26
27template<typename T>
28void decode(const nlohmann::json& json, std::vector<T>& value);
29
30template<std::size_t Index = 0, typename... T>
31void decode(const nlohmann::json& json, std::variant<T...>& value);
32
33template<typename T>
34bool matchesDiscriminator(const nlohmann::json& json) {
35 if constexpr (requires { typename T::element_type; }) {
36 using Element = typename T::element_type;
37 if constexpr (requires { Element::TYPE; }) {
38 return json.is_object() && json.value("type", std::string { }) == Element::TYPE;
39 }
40 if constexpr (requires { Element::STATUS; }) {
41 return json.is_object() && json.value("status", std::string { }) == Element::STATUS;
42 }
43 if constexpr (requires { Element::SOURCE; }) {
44 return json.is_object() && json.value("source", std::string { }) == Element::SOURCE;
45 }
46 }
47
48 return true;
49}
50
51template<typename T>
52void decode(const nlohmann::json& json, T& value) {
53 json.get_to(value);
54}
55
56template<typename T>
57void decode(const nlohmann::json& json, std::shared_ptr<T>& value) {
58 if (json.is_null()) {
59 throw std::invalid_argument("Required Telegram object is null");
60 }
61 value = std::make_shared<T>();
62 from_json(json, *value);
63}
64
65template<typename T>
66void decode(const nlohmann::json& json, std::optional<T>& value) {
67 if (json.is_null()) {
68 value.reset();
69 return;
70 }
71 value.emplace();
72 decode(json, *value);
73}
74
75template<typename T>
76void decode(const nlohmann::json& json, std::vector<T>& value) {
77 if (!json.is_array()) {
78 throw std::invalid_argument("Telegram array field is not an array");
79 }
80 value.clear();
81 value.reserve(json.size());
82 for (const auto& item : json) {
83 T decoded { };
84 decode(item, decoded);
85 value.push_back(std::move(decoded));
86 }
87}
88
89template<std::size_t Index, typename... T>
90void decode(const nlohmann::json& json, std::variant<T...>& value) {
91 if constexpr (Index == sizeof...(T)) {
92 throw std::invalid_argument("JSON value does not match Telegram union");
93 } else {
94 try {
95 using Candidate = std::variant_alternative_t<Index, std::variant<T...>>;
96 if (!matchesDiscriminator<Candidate>(json)) {
97 throw std::invalid_argument("Telegram union discriminator does not match");
98 }
99 Candidate candidate { };
100 decode(json, candidate);
101 value = std::move(candidate);
102 } catch (const std::exception&) {
103 decode<Index + 1>(json, value);
104 }
105 }
106}
107
108template<typename T>
109nlohmann::json encode(const T& value);
110
111template<typename T>
112nlohmann::json encode(const std::shared_ptr<T>& value);
113
114template<typename T>
115nlohmann::json encode(const std::optional<T>& value);
116
117template<typename T>
118nlohmann::json encode(const std::vector<T>& value);
119
120template<typename... T>
121nlohmann::json encode(const std::variant<T...>& value);
122
123template<typename T>
124nlohmann::json encode(const T& value) {
125 return nlohmann::json(value);
126}
127
128template<typename T>
129nlohmann::json encode(const std::shared_ptr<T>& value) {
130 if (!value) {
131 return nullptr;
132 }
133 nlohmann::json json;
134 to_json(json, *value);
135
136 return json;
137}
138
139template<typename T>
140nlohmann::json encode(const std::optional<T>& value) {
141 return value ? encode(*value) : nlohmann::json(nullptr);
142}
143
144template<typename T>
145nlohmann::json encode(const std::vector<T>& value) {
146 nlohmann::json json = nlohmann::json::array();
147 for (const auto& item : value) {
148 json.push_back(encode(item));
149 }
150
151 return json;
152}
153
154template<typename... T>
155nlohmann::json encode(const std::variant<T...>& value) {
156 return std::visit(
157 [](const auto& item) {
158 return encode(item);
159 },
160 value);
161}
162
163template<typename T>
164void readRequiredField(const nlohmann::json& json, const char* name, T& value) {
165 decode(json.at(name), value);
166}
167
168template<typename T>
169void readOptionalField(const nlohmann::json& json, const char* name, T& value) {
170 const auto item = json.find(name);
171 if (item == json.end() || item->is_null()) {
172 value = T { };
173 return;
174 }
175 decode(*item, value);
176}
177
178template<typename T>
179void writeRequiredField(nlohmann::json& json, const char* name, const T& value) {
180 json[name] = encode(value);
181}
182
183template<typename T>
184void writeOptionalField(nlohmann::json& json, const char* name, const std::optional<T>& value) {
185 if (value) {
186 json[name] = encode(value);
187 }
188}
189
190template<typename T>
191void writeOptionalField(nlohmann::json& json, const char* name, const std::shared_ptr<T>& value) {
192 if (value) {
193 json[name] = encode(value);
194 }
195}
196
197} // namespace TgBot::Json
198
TGBOT_API void to_json(nlohmann::json &json, const AcceptedGiftTypes &value)
TGBOT_API void from_json(const nlohmann::json &json, AcceptedGiftTypes &value)