11#ifndef BASE_AUTO_JSON_H
22#define BASE_AUTO_JSON_H
33
4+ #include < cassert>
45#include < initializer_list>
56#include < optional>
67#include < ranges>
@@ -28,8 +29,7 @@ concept Optional =
2829 *val;
2930 };
3031
31- template <class T >
32- concept Tuple = sizeof (std::tuple_size<T>) > 0 ;
32+ template <class T > concept Tuple = sizeof (std::tuple_size<T>) > 0 ;
3333
3434template <class T >
3535concept Pair = Tuple<T> && std::tuple_size_v<T> == 2 ;
@@ -46,67 +46,25 @@ concept SerializableRange =
4646
4747struct JSON
4848{
49- template <class IL > void closeOpenObj (IL &&il)
49+ template <class T > inline void primitive ( const T &val) const
5050 {
51- auto from = std::begin (il);
52- auto end = std::end (il);
53-
54- if (!std::empty (curr)) {
55- auto [pre , cur] = std::ranges::mismatch (curr, il);
56- if (auto cend = std::end (curr); pre != cend) [[likely]]
57- json.append (cend - pre - 1 , ' }' );
58- else {
59- if (cur == end) {
60- throw std::logic_error (
61- " Same object but multiple base classes are "
62- " pure serializable." );
63- }
64- throw std::logic_error (
65- " An already serialized object member is not "
66- " serializable." );
67- }
68- json += ' ,' ;
69- from = cur;
70- }
71- else {
72- json += " {" ;
51+ if constexpr (std::is_arithmetic_v<T>) {
52+ json += toString (val);
7353 }
74-
75- if (from != end) [[likely]] {
76- for (--end; from != end; ++from) {
77- json += ' \" ' ;
78- json += Text::SmartString::escape (std::string{*from});
79- json += " \" :{" ;
80- }
54+ else if constexpr (std::is_enum_v<T>
55+ || std::is_same_v<T, bool >) {
56+ json += ' \" ' ;
57+ json += toString (val);
8158 json += ' \" ' ;
82- json += Text::SmartString::escape (std::string{*end});
83- json += " \" :" ;
84- }
85- else {
86- throw std::logic_error (" Member of a serializable object "
87- " are already serialized." );
88- }
89- if constexpr (std::is_lvalue_reference_v<IL>) { curr = il; }
90- else {
91- curr = saved.emplace (std::forward<IL>(il));
92- }
93- }
94-
95- template <class T > inline void primitive (T &&val)
96- {
97- if constexpr (std::is_arithmetic_v<
98- std::remove_reference_t <T>>) {
99- json += toString (std::forward<T>(val));
10059 }
10160 else {
10261 json += ' \" ' ;
103- json += Text::SmartString::escape (
104- toString (std::forward<T>(val)));
62+ json += Text::SmartString::escape (toString (val));
10563 json += ' \" ' ;
10664 }
10765 }
10866
109- template <class T > inline void array (T && val)
67+ template <class T > inline void array (const T &val) const
11068 {
11169 json += ' [' ;
11270 bool not_first = false ;
@@ -120,7 +78,24 @@ struct JSON
12078 json += ' ]' ;
12179 }
12280
123- template <class T > inline void any (T &&val)
81+ template <class T > inline void tupleObj (const T &val) const
82+ {
83+ std::apply (
84+ [this ](const auto &arg, const auto &...args )
85+ {
86+ json += ' [' ;
87+ any (arg);
88+ ((json += ' ,' , any (args)), ...);
89+ json += ' ]' ;
90+ },
91+ val);
92+ }
93+
94+ template <class T > void dynamicObj (const T &val) const ;
95+
96+ template <class T > void staticObj (const T &val) const ;
97+
98+ template <class T > inline void any (const T &val) const
12499 {
125100 if constexpr (JSONSerializable<T>) { json += val.toJSON (); }
126101 else if constexpr (std::is_same_v<std::remove_cvref_t <T>,
@@ -136,75 +111,152 @@ struct JSON
136111 any (*val);
137112 }
138113 else if constexpr (StringConvertable<T>) {
139- primitive (std::forward<T>( val) );
114+ primitive (val);
140115 }
141116 else if constexpr (SerializableRange<T>) {
142117 if constexpr (Pair<std::ranges::range_value_t <T>>) {
143- if (std::empty (val)) {
144- json += " {}" ;
145- } else {
146- JSON j{json};
147- bool which{};
148- std::array<std::string, 2 > strings;
149- for (const auto & [k, v] : val) {
150- j (v, {strings[which ^= true ] = toString (k)});
151- }
152- }
153- } else {
154- array (std::forward<T>(val));
118+ dynamicObj (val);
119+ }
120+ else {
121+ array (val);
155122 }
156123 }
157124 else if constexpr (Tuple<T>) {
158- std::apply ([this ] (auto && arg, auto && ... args) {
159- json += ' [' ;
160- any (std::forward<decltype (arg)>(arg));
161- ((json += ' ,' , any (std::forward<decltype (args)>(args))), ...);
162- json += ' ]' ;
163- }, std::forward<T>(val));
125+ tupleObj (val);
164126 }
165127 else {
166- Refl::visit (JSON{json}, val);
128+ staticObj ( val);
167129 }
168130 }
169131
170- template <class T >
171- inline JSON &operator ()(T &&val,
172- std::initializer_list<std::string_view> &&il)
132+ explicit inline JSON (std::string &json) : json(json) {}
133+
134+ std::string &json;
135+ };
136+
137+ struct JSONAutoObj : JSON
138+ {
139+ using JSON::JSON;
140+
141+ inline ~JSONAutoObj ()
173142 {
174- closeOpenObj (
175- std::forward<std::initializer_list<std::string_view>>(
176- il));
177- any (std::forward<T>(val));
178- return *this ;
143+ if (cp)
144+ json.append (std::size (*cp), ' }' );
145+ else
146+ json += " {}" ;
147+ }
148+
149+ inline void closeOpenObj (
150+ const std::initializer_list<std::string_view> &il)
151+ {
152+ const auto *from = std::begin (il);
153+ const auto *end = std::end (il);
154+
155+ if (cp) {
156+ auto [pre , cur] = std::ranges::mismatch (*cp, il);
157+ assert (pre != std::end (*cp));
158+ json.append (std::end (*cp) - pre - 1 , ' }' );
159+ json += ' ,' ;
160+ from = cur;
161+ }
162+ else
163+ json += ' {' ;
164+
165+ assert (from != end);
166+ while (true ) {
167+ json += ' \" ' ;
168+ json.append (*from);
169+ json += " \" :" ;
170+ if (++from != end)
171+ json += ' {' ;
172+ else
173+ break ;
174+ }
175+ cp = &il;
179176 }
180177
181178 template <class T >
182179 requires (JSONSerializable<T> || Optional<T>
183- || StringConvertable<T> || SerializableRange<T> ||
184- Tuple<T>)
185- inline JSON & operator ()(T & &val,
180+ || StringConvertable<T> || SerializableRange<T>
181+ || Tuple<T>)
182+ inline void operator ()(const T &val,
186183 const std::initializer_list<std::string_view> &il)
187184 {
188185 closeOpenObj (il);
189- any (std::forward<T>(val));
190- return *this ;
186+ any (val);
191187 }
192188
193- JSON (std::string &json) : json(json) {}
189+ template <class T >
190+ inline void operator ()(const T &val,
191+ std::initializer_list<std::string_view> &&il) = delete;
192+
193+ const std::initializer_list<std::string_view> *cp{};
194+ };
194195
195- ~JSON ()
196+ struct JSONObj : JSON
197+ {
198+ using JSON::JSON;
199+
200+ inline ~JSONObj ()
196201 {
197- if (!std::empty (curr)) json.append (std::size (curr), ' }' );
202+ if (!was) json += ' {' ;
203+ json += ' }' ;
198204 }
199- std::string &json;
200- std::optional<std::vector<std::string_view>> saved;
201- std::span<const std::string_view> curr;
205+
206+ template <bool KeyNoEscape = true >
207+ inline void key (std::string_view key)
208+ {
209+ json += std::exchange (was, true ) ? ' ,' : ' {' ;
210+
211+ json += ' \" ' ;
212+ if constexpr (KeyNoEscape) { json.append (key); }
213+ else {
214+ json += Text::SmartString::escape (std::string{key});
215+ }
216+ json += " \" :" ;
217+ }
218+
219+ template <bool KeyNoEscape = true >
220+ inline JSONObj nested (std::string_view key)
221+ {
222+ this ->key <KeyNoEscape>(key);
223+ return JSONObj{json};
224+ }
225+
226+ template <bool KeyNoEscape = true >
227+ inline JSONObj &raw (std::string_view key, const std::string &str)
228+ {
229+ this ->key <KeyNoEscape>(key);
230+ json += str;
231+ return *this ;
232+ }
233+
234+ template <bool KeyNoEscape = true , class T >
235+ inline JSONObj &operator ()(std::string_view key, const T &val)
236+ {
237+ this ->key <KeyNoEscape>(key);
238+ any (val);
239+ return *this ;
240+ }
241+
242+ bool was{};
202243};
203244
204- template <class T > std::string toJSON (T &&v)
245+ template <class T > inline void JSON::dynamicObj (const T &val) const
246+ {
247+ JSONObj j{json};
248+ for (const auto &[k, v] : val) { j (toString (k), v); }
249+ }
250+
251+ template <class T > inline void JSON::staticObj (const T &val) const
252+ {
253+ Refl::visit (JSONAutoObj{json}, val);
254+ }
255+
256+ template <class T > inline std::string toJSON (const T &v)
205257{
206258 std::string res;
207- JSON{res}.any (std::forward<T>(v) );
259+ JSON{res}.any (v );
208260 return res;
209261}
210262}
0 commit comments