cpp-reflect
C++ Reflection and Annotations Library
Loading...
Searching...
No Matches
json.cppm
Go to the documentation of this file.
1// Copyright (c) 2024-2025, Víctor Castillo Agüero.
2// SPDX-License-Identifier: GPL-3.0-or-later
3
8
9module;
10#include <nlohmann/json.hpp>
11
13
14import std;
15
16import packtl;
17import reflect;
18
20
21using JSON = nlohmann::json;
22
23export namespace formats {
24 template <typename O>
25 struct json_fmt: refl::visitor<json_fmt<O>> {
26 struct args_t {
27 bool pretty = false;
28 unsigned int indent = 2;
29 };
30
31 explicit json_fmt(O& out_, args_t args_)
32 : refl::visitor<json_fmt<O>>(),
33 out(out_),
34 args(args_) {}
35
36 template <typename T>
37 void handle_pointer(const T* it) {
38 if constexpr (std::same_as<T, char>) {
39 this->handle_value(it);
40 return;
41 }
42
43 current() = "reference";
44 // this->visit_pointer(it);
45 }
46
47 template <typename T>
48 void handle_reference(const T& it) {
49 current() = "reference";
50 // this->visit_pointer(it);
51 }
52
53 template <typename T>
54 void handle_value(const T& it) {
55 if constexpr (std::is_same_v<T, std::atomic_flag>) {
56 current() = (it.test() ? "SET" : "CLEAR");
57 } else if constexpr (packtl::is_type<std::unique_ptr, T>::value) {
58 const auto* value = it.get();
59 this->handle_value(*value);
60 return;
61 } else if constexpr (packtl::is_type<std::shared_ptr, T>::value) {
62 if (current_policy == serialize::policy::deep) {
63 const auto* value = it.get();
64 this->handle_value(*value);
65 return;
66 } else {
67 current() = "reference";
68 return;
69 }
70 } else if constexpr (packtl::is_type<std::weak_ptr, T>::value) {
71 if (it.expired()) {
72 current() = "null";
73 return;
74 } else if (current_policy == serialize::policy::deep) {
75 const auto* value = it.get();
76 this->handle_value(*value);
77 return;
78 } else {
79 current() = "reference";
80 return;
81 }
82 } else if constexpr (refl::Reflected<T>) {
83 if (visited_.contains((std::size_t)&it)) {
84 out << "<circular reference>";
85 return;
86 }
87 visited_.emplace((std::size_t)&it);
88 } else if constexpr (std::is_convertible_v<T, std::string>) {
89 current() = std::format("{}", std::string{it});
90 } else if constexpr (std::same_as<T, char*>) {
91 current() = std::format("{}", std::string{it});
92 return;
93 } else if constexpr (std::same_as<T, const char*>) {
94 current() = std::format("{}", std::string{it});
95 return;
96 } else if constexpr (std::same_as<T, int> or std::same_as<T, unsigned int> or
97 std::same_as<T, short> or std::same_as<T, unsigned short> or
98 std::same_as<T, long> or std::same_as<T, unsigned long> or
99 std::same_as<T, float> or std::same_as<T, double>) {
100 current() = it;
101 } else if constexpr (std::same_as<T, bool>) {
102 current() = std::format("{}", it ? "true" : "false");
103 } else if constexpr (std::formattable<T, char>) {
104 current() = std::format("{}", it);
105 }
106
107 this->visit_value(it);
108 }
109
110 template <typename T>
111 void handle_iterable(const T& iterable) {
112 if constexpr (std::__is_std_pair<typename T::value_type>) {
113 using type = typename T::value_type;
114 if constexpr (std::same_as<typename type::first_type, std::string> or
115 std::same_as<typename type::first_type, const std::string>) {
116 current() = JSON::object({});
117 for (const auto& [first, second]: iterable) {
118 current()[first] = JSON{};
119 push(current()[first]);
120 this->handle_value(second);
121 pop();
122 }
123 return;
124 }
125 }
126 current() = JSON::array({});
127 push(current());
128 this->visit_iterable(iterable);
129 pop();
130 }
131
132 template <typename T>
133 void handle_iterable_element(const T& element) {
134 current().push_back(JSON{});
135 push(current().back());
136 this->visit_iterable_element(element);
137 pop();
138 }
139
140 template <typename T>
141 void handle_tuple(const T& tuple) {
142 if constexpr (std::__is_std_pair<T> and std::same_as<typename T::first_type, std::string>) {
143 current() = JSON::object({});
144 current()[tuple.first] = JSON{};
145 push(current()[tuple.first]);
146 this->handle_value(tuple.second);
147 pop();
148 } else {
149 current() = JSON::array({});
150 push(current());
151 this->visit_tuple(tuple);
152 pop();
153 }
154 }
155
156 template <typename T>
157 void handle_tuple_element(const T& element) {
158 current().push_back(JSON{});
159 push(current().back());
160 this->visit_tuple_element(element);
161 pop();
162 }
163
164 template <typename T, typename Field>
165 void handle_field(const T& obj) {
166 std::string field_name = Field::name;
167 if constexpr (Field::template has_metadata<serialize::name>) {
168 field_name = Field::template get_metadata<serialize::name>.value;
169 }
170
171 current_policy = serialize::policy::shallow;
172 if constexpr (Field::template has_metadata<serialize::policy::policy_e>) {
173 current_policy = Field::template get_metadata<serialize::policy::policy_e>;
174 if constexpr (Field::template get_metadata<serialize::policy::policy_e> ==
176 current()[field_name] = JSON{};
177 push(current()[field_name]);
178
179 if constexpr (Field::is_reference) {
180 const auto& it = Field::from_instance(obj);
181 this->handle_value(it);
182 } else if constexpr (Field::is_pointer) {
183 const auto* it = Field::from_instance(obj);
184 this->handle_value(*it);
185 } else {
186 const auto& it = Field::from_instance(obj);
187 this->handle_value(it);
188 }
189
190 pop();
191 } else if constexpr (Field::template get_metadata<serialize::policy::policy_e> ==
193 current()[field_name] = JSON{};
194 push(current()[field_name]);
195
196 if constexpr (Field::is_reference) {
197 current() = "reference";
198 } else if constexpr (Field::is_pointer) {
199 current() = "reference";
200 } else {
201 const auto& it = Field::from_instance(obj);
202 this->handle_value(it);
203 }
204
205 pop();
206 } else if constexpr (Field::template get_metadata<serialize::policy::policy_e> ==
208 // do nothing
209 }
210 } else {
211 current()[field_name] = JSON{};
212 push(current()[field_name]);
213
214 this->template visit_obj_field<T, Field>(obj);
215
216 pop();
217 }
218 }
219
220 template <typename T>
221 void handle_obj(const T& obj) {
222 // current()["__type"] = refl::type_name<T>;
223 this->visit_obj(obj);
224 }
225
226 template <refl::Reflected R>
227 void serialize(const R& obj) {
228 json_ = JSON::object({});
229 push(json_);
230 this->visit(obj);
231 out << json_.dump(args.pretty ? args.indent : -1);
232 }
233
234 private:
235 JSON& current() {
236 return *obj_stack.top();
237 }
238
239 void push(JSON& obj) {
240 obj_stack.push(&obj);
241 }
242
243 void pop() {
244 obj_stack.pop();
245 }
246
247 private:
248 std::unordered_set<std::size_t> visited_{};
249 O& out;
250 args_t args;
251 JSON json_;
252 std::stack<JSON*> obj_stack;
253
255 };
256} // namespace formats
nlohmann::json JSON
Definition json.cppm:21
unsigned int indent
Definition json.cppm:28
void serialize(const R &obj)
Definition json.cppm:227
void handle_field(const T &obj)
Definition json.cppm:165
json_fmt(O &out_, args_t args_)
Definition json.cppm:31
void handle_pointer(const T *it)
Definition json.cppm:37
void handle_value(const T &it)
Definition json.cppm:54
void handle_tuple(const T &tuple)
Definition json.cppm:141
void handle_tuple_element(const T &element)
Definition json.cppm:157
void handle_iterable(const T &iterable)
Definition json.cppm:111
void handle_obj(const T &obj)
Definition json.cppm:221
void handle_iterable_element(const T &element)
Definition json.cppm:133
void handle_reference(const T &it)
Definition json.cppm:48
void visit_obj_field(const R &obj)
Definition visitor.cppm:114
void visit_obj(const R &obj)
Definition visitor.cppm:103
void visit_value(const T &obj)
Definition visitor.cppm:84
void visit_iterable_element(const T &item)
Definition visitor.cppm:136
void visit_tuple_element(const T &item)
Definition visitor.cppm:158
void visit_tuple(const T &it)
Definition visitor.cppm:151
void visit_iterable(const T &iterable)
Definition visitor.cppm:128
void visit(const R &obj)
Definition visitor.cppm:32