header_utils
Loading...
Searching...
No Matches
json_helpers.h
1
4
5#pragma once
6
7#include <nlohmann/json.hpp>
8#include <fstream>
9#include "string_ops.h"
10#include "mmap.h"
11#include "expected.h"
12
13namespace ghassanpl::formats
14{
15
16 namespace text
17 {
21
24 inline std::string load_file(std::filesystem::path const& from, std::error_code& ec)
25 {
26 auto source = ghassanpl::make_mmap_source<char>(from, ec);
27 return ec ? std::string{} : std::string{ source.begin(), source.end() };
28 }
29
32 inline std::string load_file(std::filesystem::path const& from)
33 {
34 std::error_code ec;
35 auto source = ghassanpl::make_mmap_source<char>(from, ec);
36 if (ec)
37 throw std::runtime_error(format("file '{}' not found", from.string()));
38 return std::string{ source.begin(), source.end() };
39 }
40
42 inline expected<std::string, std::error_code> try_load_file(std::filesystem::path const& from)
43 {
44 std::error_code ec;
45 auto result = load_file(from, ec);
46 return ec ? unexpected(ec) : expected<std::string, std::error_code>{ std::move(result) };
47 }
48
49 inline bool save_file(std::filesystem::path const& to, std::string_view string, std::error_code& ec)
50 {
52 std::ofstream out{ to };
53 out.write(string.data(), string.size());
54 return out.fail();
55 }
56
57 inline void save_file(std::filesystem::path const& to, std::string_view string)
58 {
59 std::ofstream out{ to };
60 out.exceptions(std::ios::badbit | std::ios::failbit);
61 out.write(string.data(), string.size());
62 }
64 }
65
66 namespace text_lines
67 {
68
73
74 inline std::vector<std::string> load_file(std::filesystem::path const& from, std::error_code& ec)
75 {
76 auto source = ghassanpl::make_mmap_source<char>(from, ec);
77 if (ec)
78 return {};
79
80 std::vector<std::string> result;
81 ghassanpl::string_ops::split({ source.begin(), source.end() }, '\n', [&](std::string_view line, bool) { result.emplace_back(line); });
82 return result;
83 }
84
85 inline std::vector<std::string> load_file(std::filesystem::path const& from)
86 {
87 std::error_code ec;
88 auto source = ghassanpl::make_mmap_source<char>(from, ec);
89 if (ec)
90 throw std::runtime_error(format("file '{}' not found", from.string()));
91
92 std::vector<std::string> result;
93 ghassanpl::string_ops::split({ source.begin(), source.end() }, '\n', [&](std::string_view line, bool) { result.emplace_back(line); });
94 return result;
95 }
96
97 template <typename CALLBACK>
98 requires std::invocable<CALLBACK, std::string_view>
99 inline void load_file(std::filesystem::path const& from, CALLBACK&& callback)
100 {
101 std::error_code ec;
102 auto source = ghassanpl::make_mmap_source<char>(from, ec);
103 if (ec)
104 throw std::runtime_error(format("file '{}' not found", from.string()));
105
106 std::vector<std::string> result;
107 ghassanpl::string_ops::split({ source.begin(), source.end() }, '\n', [&](std::string_view line, bool) { callback(line); });
108 return result;
109 }
110
111 inline std::vector<std::string> try_load_file(std::filesystem::path const& from)
112 {
113 std::error_code ec;
114 return load_file(from, ec);
115 }
116
117 template <std::ranges::range T>
118 inline bool save_file(std::filesystem::path const& to, T string_range, std::error_code& ec)
119 {
121 std::ofstream out{ to };
122 for (auto& string : string_range)
123 {
124 out.write(std::to_address(std::ranges::begin(string)), std::ranges::size(string));
125 out << "\n";
126 }
127 return out.fail();
128 }
129
130 template <std::ranges::range T>
131 inline void save_file(std::filesystem::path const& to, T string_range)
132 {
133 std::ofstream out{ to };
134 out.exceptions(std::ios::badbit | std::ios::failbit);
135 for (auto& string : string_range)
136 {
137 out.write(std::to_address(std::ranges::begin(string)), std::ranges::size(string));
138 out << "\n";
139 }
140 }
142 }
143
144 namespace json
145 {
149
150 inline const nlohmann::json empty_json = nlohmann::json{};
151 inline const nlohmann::json empty_json_array = nlohmann::json::array();
152 inline const nlohmann::json empty_json_object = nlohmann::json::object();
153
154 inline nlohmann::json load_file(std::filesystem::path const& from, std::error_code& ec)
155 {
156 auto source = ghassanpl::make_mmap_source<char>(from, ec);
157 return ec ? nlohmann::json{} : nlohmann::json::parse(source);
158 }
159
160 inline nlohmann::json try_load_file(std::filesystem::path const& from, nlohmann::json const& or_json = empty_json)
161 {
162 std::error_code ec;
163 auto source = ghassanpl::make_mmap_source<char>(from, ec);
164 return ec ? or_json : nlohmann::json::parse(source);
165 }
166
167 inline nlohmann::json load_file(std::filesystem::path const& from)
168 {
169 try
170 {
171 return nlohmann::json::parse(ghassanpl::make_mmap_source<char>(from));
172 }
173 catch (...)
174 {
175 std::throw_with_nested(std::runtime_error{ format("while trying to load json file {}", from.string()) });
176 }
177 }
178
179 inline void save_file(std::filesystem::path const& to, nlohmann::json const& j, bool pretty = true)
180 {
181 std::ofstream out{ to };
182 nlohmann::detail::serializer<nlohmann::json> s{ nlohmann::detail::output_adapter<char, std::string>(out), '\t', nlohmann::detail::error_handler_t::strict };
183 s.dump(j, pretty, false, 1);
184 }
185
187 using jtype = nlohmann::json::value_t;
188
191 inline nlohmann::json const& get(nlohmann::json const& g, std::string_view key, jtype type = jtype::discarded)
192 {
193 if (auto it = g.find(key); it != g.end() && (type == jtype::discarded || it->type() == type))
194 return *it;
195 return json::empty_json;
196 }
197
200 inline std::string get(nlohmann::json const& g, std::string_view key, std::string_view default_value, jtype type = jtype::discarded)
201 {
202 if (auto it = g.find(key); it != g.end() && (type == jtype::discarded || it->type() == type))
203 return (std::string)*it;
204 return std::string{ default_value };
205 }
206
209 template <std::integral T>
210 inline T get(nlohmann::json const& g, std::string_view key, T default_value, jtype type = jtype::discarded)
211 {
212 if (auto it = g.find(key); it != g.end() && (type == jtype::discarded || it->type() == type))
213 return (T)*it;
214 return default_value;
215 }
216
219 template <std::floating_point T>
220 inline T get(nlohmann::json const& g, std::string_view key, T default_value, jtype type = jtype::discarded)
221 {
222 if (auto it = g.find(key); it != g.end() && (type == jtype::discarded || it->type() == type))
223 return (T)*it;
224 return default_value;
225 }
226
228 inline nlohmann::json const& get_array(nlohmann::json const& g, std::string_view key)
229 {
230 if (auto it = g.find(key); it != g.end() && it->type() == jtype::array)
231 return *it;
232 return json::empty_json_array;
233 }
234
237 template <typename T>
238 inline void field(T& val, nlohmann::json const& g, std::string_view key)
239 {
240 try
241 {
242 auto it = g.find(key);
243 if (it != g.end())
244 {
245 val = *it;
246 return;
247 }
248 }
249 catch (...)
250 {
251 std::throw_with_nested(std::runtime_error{ std::format("while trying to convert value at key \"{}\" to type {}", key, typeid(T).name()) });
252 }
253
254 throw std::runtime_error(std::format("no key \"{}\" found", key));
255 }
256
259 template <typename T>
260 inline void field(T& val, nlohmann::json const& g, size_t key)
261 {
262 try
263 {
264 val = g.at(key);
265 return;
266 }
267 catch (...)
268 {
269 std::throw_with_nested(std::runtime_error{ std::format("while trying to convert value at element {} to type {}", key, typeid(T).name()) });
270 }
271 }
272
275 template <typename T>
276 inline bool field_opt(T& val, nlohmann::json const& g, std::string_view key)
277 {
278 try
279 {
280 auto it = g.find(key);
281 if (it != g.end())
282 {
283 val = *it;
284 return true;
285 }
286 }
287 catch (...)
288 {
289 std::throw_with_nested(std::runtime_error{ std::format("while trying to convert value at key \"{}\" to type {}", key, typeid(T).name()) });
290 }
291 return false;
292 }
293
295 template <typename VISIT_FUNC>
296 auto visit(nlohmann::json const& j, VISIT_FUNC&& func)
297 {
298 switch (j.type())
299 {
300 using enum nlohmann::detail::value_t;
301 case object: return func(j.get_ref<nlohmann::json::object_t const&>());
302 case array: return func(j.get_ref<nlohmann::json::array_t const&>());
303 case string: return func(j.get_ref<nlohmann::json::string_t const&>());
304 case boolean: return func(j.get_ref<nlohmann::json::boolean_t const&>());
305 case number_integer: return func(j.get_ref<nlohmann::json::number_integer_t const&>());
306 case number_unsigned: return func(j.get_ref<nlohmann::json::number_unsigned_t const&>());
307 case number_float: return func(j.get_ref<nlohmann::json::number_float_t const&>());
308 case binary: return func(j.get_ref<nlohmann::json::binary_t const&>());
309 default:
310 return func(nullptr);
311 }
312 }
313
314 constexpr const char* type_name(nlohmann::json::value_t type) noexcept
315 {
316 switch (type)
317 {
318 using enum nlohmann::detail::value_t;
319 case null:
320 return "null";
321 case object:
322 return "object";
323 case array:
324 return "array";
325 case string:
326 return "string";
327 case boolean:
328 return "boolean";
329 case binary:
330 return "binary";
331 case discarded:
332 return "discarded";
333 default:
334 return "number";
335 }
336 }
338 }
339
340 namespace ubjson
341 {
345 inline nlohmann::json try_load_file(std::filesystem::path const& from, nlohmann::json const& or_json = json::empty_json)
346 {
347 std::error_code ec;
348 auto source = ghassanpl::make_mmap_source<char>(from, ec);
349 return ec ? or_json : nlohmann::json::from_ubjson(source);
350 }
351
352 inline nlohmann::json load_file(std::filesystem::path const& from, std::error_code& ec)
353 {
354 auto source = ghassanpl::make_mmap_source<char>(from, ec);
355 return ec ? nlohmann::json{} : nlohmann::json::from_ubjson(source);
356 }
357
358 inline nlohmann::json load_file(std::filesystem::path const& from)
359 {
360 try
361 {
362 return nlohmann::json::from_ubjson(ghassanpl::make_mmap_source<char>(from));
363 }
364 catch (...)
365 {
366 std::throw_with_nested(std::runtime_error{ format("while trying to load ubjson file {}", from.string()) });
367 }
368 }
369
370 inline void save_file(std::filesystem::path const& to, nlohmann::json const& j)
371 {
372 std::ofstream out{ to };
373 nlohmann::json::to_ubjson(j, nlohmann::detail::output_adapter<char, std::string>(out), true, true);
374 }
376 }
377
378 namespace cbor
379 {
383
385 inline nlohmann::json try_load_file(std::filesystem::path const& from, nlohmann::json const& or_json = json::empty_json)
386 {
387 std::error_code ec;
388 auto source = ghassanpl::make_mmap_source<char>(from, ec);
389 return ec ? or_json : nlohmann::json::from_cbor(source);
390 }
391
393 inline void save_file(std::filesystem::path const& to, nlohmann::json const& j)
394 {
395 std::ofstream out{ to };
396 nlohmann::json::to_cbor(j, nlohmann::detail::output_adapter<char, std::string>(out));
397 }
399 }
400
401}
constexpr auto bit_count
Equal to the number of bits in the type.
Definition bits.h:33
nlohmann::json try_load_file(std::filesystem::path const &from, nlohmann::json const &or_json=json::empty_json)
Tries loading a CBOR file.
void save_file(std::filesystem::path const &to, nlohmann::json const &j)
Saves a CBOR file.
nlohmann::json::value_t jtype
Smaller name for nlohmann::json::value_t.
bool field_opt(T &val, nlohmann::json const &g, std::string_view key)
Same as field() but returns if it succeeded, instead of throwing.
void field(T &val, nlohmann::json const &g, std::string_view key)
Sets the value of val to the item in json object g with key key
nlohmann::json const & get_array(nlohmann::json const &g, std::string_view key)
Gets the array value in the json object g with the key key, or an empty array if none found.
nlohmann::json const & get(nlohmann::json const &g, std::string_view key, jtype type=jtype::discarded)
Gets the item in the json object g with the key key, or an empty json object if none found.
auto visit(nlohmann::json const &j, VISIT_FUNC &&func)
Calls func with the actual value inside j; similar to std::visit
constexpr CONTAINER to(RANGE &&range, TYPES &&... args)
to<container>();
Definition ranges.h:369
constexpr void split(std::string_view source, char delim, FUNC &&func) noexcept(noexcept(func(std::string_view{}, true)))
Performs a basic "split" operation, calling func for each part of source delimited by delim.
Definition string_ops.h:955
std::string load_file(std::filesystem::path const &from, std::error_code &ec)
Returns the contents of a text file as a string.
expected< std::string, std::error_code > try_load_file(std::filesystem::path const &from)
Returns the contents of a text file as a string.