simdjson 4.0.7
Ridiculously Fast JSON
Loading...
Searching...
No Matches
json_builder.h
1
5#ifndef SIMDJSON_GENERIC_BUILDER_H
6
7#ifndef SIMDJSON_CONDITIONAL_INCLUDE
8#define SIMDJSON_GENERIC_STRING_BUILDER_H
9#include "simdjson/generic/builder/json_string_builder.h"
10#include "simdjson/concepts.h"
11#endif // SIMDJSON_CONDITIONAL_INCLUDE
12#if SIMDJSON_STATIC_REFLECTION
13
14#include <charconv>
15#include <cstring>
16#include <meta>
17#include <memory>
18#include <optional>
19#include <string_view>
20#include <type_traits>
21#include <utility>
22// #include <static_reflection> // for std::define_static_string - header not available yet
23
24namespace simdjson {
25namespace SIMDJSON_IMPLEMENTATION {
26namespace builder {
27
28template <class T>
29 requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
30constexpr void atom(string_builder &b, const T &t) {
31 auto it = t.begin();
32 auto end = t.end();
33 if (it == end) {
34 b.append_raw("[]");
35 return;
36 }
37 b.append('[');
38 atom(b, *it);
39 ++it;
40 for (; it != end; ++it) {
41 b.append(',');
42 atom(b, *it);
43 }
44 b.append(']');
45}
46
47template <class T>
48 requires(std::is_same_v<T, std::string> ||
49 std::is_same_v<T, std::string_view> ||
50 std::is_same_v<T, const char *> ||
51 std::is_same_v<T, char>)
52constexpr void atom(string_builder &b, const T &t) {
53 b.escape_and_append_with_quotes(t);
54}
55
56template <concepts::string_view_keyed_map T>
57 requires(!require_custom_serialization<T>)
58constexpr void atom(string_builder &b, const T &m) {
59 if (m.empty()) {
60 b.append_raw("{}");
61 return;
62 }
63 b.append('{');
64 bool first = true;
65 for (const auto& [key, value] : m) {
66 if (!first) {
67 b.append(',');
68 }
69 first = false;
70 // Keys must be convertible to string_view per the concept
71 b.escape_and_append_with_quotes(key);
72 b.append(':');
73 atom(b, value);
74 }
75 b.append('}');
76}
77
78
79template<typename number_type,
80 typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
81constexpr void atom(string_builder &b, const number_type t) {
82 b.append(t);
83}
84
85template <class T>
86 requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
87 !concepts::string_view_keyed_map<T> &&
88 !concepts::optional_type<T> &&
89 !concepts::smart_pointer<T> &&
90 !concepts::appendable_containers<T> &&
91 !std::is_same_v<T, std::string> &&
92 !std::is_same_v<T, std::string_view> &&
93 !std::is_same_v<T, const char*> &&
94 !std::is_same_v<T, char> && !require_custom_serialization<T>)
95constexpr void atom(string_builder &b, const T &t) {
96 int i = 0;
97 b.append('{');
98 template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
99 if (i != 0)
100 b.append(',');
101 constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
102 b.append_raw(key);
103 b.append(':');
104 atom(b, t.[:dm:]);
105 i++;
106 };
107 b.append('}');
108}
109
110// Support for optional types (std::optional, etc.)
111template <concepts::optional_type T>
112 requires(!require_custom_serialization<T>)
113constexpr void atom(string_builder &b, const T &opt) {
114 if (opt) {
115 atom(b, opt.value());
116 } else {
117 b.append_raw("null");
118 }
119}
120
121// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
122template <concepts::smart_pointer T>
123 requires(!require_custom_serialization<T>)
124constexpr void atom(string_builder &b, const T &ptr) {
125 if (ptr) {
126 atom(b, *ptr);
127 } else {
128 b.append_raw("null");
129 }
130}
131
132// Support for enums - serialize as string representation using expand approach from P2996R12
133template <typename T>
134 requires(std::is_enum_v<T> && !require_custom_serialization<T>)
135void atom(string_builder &b, const T &e) {
136#if SIMDJSON_STATIC_REFLECTION
137 constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
138 template for (constexpr auto enum_val : enumerators) {
139 constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val)));
140 if (e == [:enum_val:]) {
141 b.append_raw(enum_str);
142 return;
143 }
144 };
145 // Fallback to integer if enum value not found
146 atom(b, static_cast<std::underlying_type_t<T>>(e));
147#else
148 // Fallback: serialize as integer if reflection not available
149 atom(b, static_cast<std::underlying_type_t<T>>(e));
150#endif
151}
152
153// Support for appendable containers that don't have operator[] (sets, etc.)
154template <concepts::appendable_containers T>
155 requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
156 !concepts::optional_type<T> && !concepts::smart_pointer<T> &&
157 !std::is_same_v<T, std::string> &&
158 !std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
159constexpr void atom(string_builder &b, const T &container) {
160 if (container.empty()) {
161 b.append_raw("[]");
162 return;
163 }
164 b.append('[');
165 bool first = true;
166 for (const auto& item : container) {
167 if (!first) {
168 b.append(',');
169 }
170 first = false;
171 atom(b, item);
172 }
173 b.append(']');
174}
175
176// append functions that delegate to atom functions for primitive types
177template <class T>
178 requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
179void append(string_builder &b, const T &t) {
180 atom(b, t);
181}
182
183template <class T>
184 requires(std::is_same_v<T, std::string> ||
185 std::is_same_v<T, std::string_view> ||
186 std::is_same_v<T, const char *> ||
187 std::is_same_v<T, char>)
188void append(string_builder &b, const T &t) {
189 atom(b, t);
190}
191
192template <concepts::optional_type T>
193 requires(!require_custom_serialization<T>)
194void append(string_builder &b, const T &t) {
195 atom(b, t);
196}
197
198template <concepts::smart_pointer T>
199 requires(!require_custom_serialization<T>)
200void append(string_builder &b, const T &t) {
201 atom(b, t);
202}
203
204template <concepts::appendable_containers T>
205 requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
206 !concepts::optional_type<T> && !concepts::smart_pointer<T> &&
207 !std::is_same_v<T, std::string> &&
208 !std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
209void append(string_builder &b, const T &t) {
210 atom(b, t);
211}
212
213template <concepts::string_view_keyed_map T>
214 requires(!require_custom_serialization<T>)
215void append(string_builder &b, const T &t) {
216 atom(b, t);
217}
218
219// works for struct
220template <class Z>
221 requires(std::is_class_v<Z> && !concepts::container_but_not_string<Z> &&
222 !concepts::string_view_keyed_map<Z> &&
223 !concepts::optional_type<Z> &&
224 !concepts::smart_pointer<Z> &&
225 !concepts::appendable_containers<Z> &&
226 !std::is_same_v<Z, std::string> &&
227 !std::is_same_v<Z, std::string_view> &&
228 !std::is_same_v<Z, const char*> &&
229 !std::is_same_v<Z, char> && !require_custom_serialization<Z>)
230void append(string_builder &b, const Z &z) {
231 int i = 0;
232 b.append('{');
233 template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) {
234 if (i != 0)
235 b.append(',');
236 constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
237 b.append_raw(key);
238 b.append(':');
239 atom(b, z.[:dm:]);
240 i++;
241 };
242 b.append('}');
243}
244
245// works for container that have begin() and end() iterators
246template <class Z>
247 requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
248void append(string_builder &b, const Z &z) {
249 auto it = z.begin();
250 auto end = z.end();
251 if (it == end) {
252 b.append_raw("[]");
253 return;
254 }
255 b.append('[');
256 atom(b, *it);
257 ++it;
258 for (; it != end; ++it) {
259 b.append(',');
260 atom(b, *it);
261 }
262 b.append(']');
263}
264
265template <class Z>
266 requires (require_custom_serialization<Z>)
267void append(string_builder &b, const Z &z) {
268 b.append(z);
269}
270
271
272template <class Z>
273simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
274 string_builder b(initial_capacity);
275 append(b, z);
276 std::string_view s;
277 if(auto e = b.view().get(s); e) { return e; }
278 return std::string(s);
279}
280
281template <class Z>
282simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
283 string_builder b(initial_capacity);
284 append(b, z);
285 std::string_view view;
286 if(auto e = b.view().get(view); e) { return e; }
287 s.assign(view);
288 return SUCCESS;
289}
290
291template <class Z>
292string_builder& operator<<(string_builder& b, const Z& z) {
293 append(b, z);
294 return b;
295}
296
297// extract_from: Serialize only specific fields from a struct to JSON
298template<constevalutil::fixed_string... FieldNames, typename T>
299 requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
300void extract_from(string_builder &b, const T &obj) {
301 // Helper to check if a field name matches any of the requested fields
302 auto should_extract = [](std::string_view field_name) constexpr -> bool {
303 return ((FieldNames.view() == field_name) || ...);
304 };
305
306 b.append('{');
307 bool first = true;
308
309 // Iterate through all members of T using reflection
310 template for (constexpr auto mem : std::define_static_array(
311 std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
312
313 if constexpr (std::meta::is_public(mem)) {
314 constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
315
316 // Only serialize this field if it's in our list of requested fields
317 if constexpr (should_extract(key)) {
318 if (!first) {
319 b.append(',');
320 }
321 first = false;
322
323 // Serialize the key
324 constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)));
325 b.append_raw(quoted_key);
326 b.append(':');
327
328 // Serialize the value
329 atom(b, obj.[:mem:]);
330 }
331 }
332 };
333
334 b.append('}');
335}
336
337template<constevalutil::fixed_string... FieldNames, typename T>
338 requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
339simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
340 string_builder b(initial_capacity);
341 extract_from<FieldNames...>(b, obj);
342 std::string_view s;
343 if(auto e = b.view().get(s); e) { return e; }
344 return std::string(s);
345}
346
347} // namespace builder
348} // namespace SIMDJSON_IMPLEMENTATION
349// Alias the function template to 'to' in the global namespace
350template <class Z>
351simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
352 SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
353 SIMDJSON_IMPLEMENTATION::builder::append(b, z);
354 std::string_view s;
355 if(auto e = b.view().get(s); e) { return e; }
356 return std::string(s);
357}
358template <class Z>
359simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
360 SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
361 SIMDJSON_IMPLEMENTATION::builder::append(b, z);
362 std::string_view view;
363 if(auto e = b.view().get(view); e) { return e; }
364 s.assign(view);
365 return SUCCESS;
366}
367// Global namespace function for extract_from
368template<constevalutil::fixed_string... FieldNames, typename T>
369 requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
370simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
371 SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
372 SIMDJSON_IMPLEMENTATION::builder::extract_from<FieldNames...>(b, obj);
373 std::string_view s;
374 if(auto e = b.view().get(s); e) { return e; }
375 return std::string(s);
376}
377
378} // namespace simdjson
379
380#endif // SIMDJSON_STATIC_REFLECTION
381
382#endif
The top level simdjson namespace, containing everything the library provides.
Definition base.h:8
simdjson_result< std::string_view > to_json_string(SIMDJSON_IMPLEMENTATION::ondemand::document &x) noexcept
Create a string-view instance out of a document instance.
@ SUCCESS
No error.
Definition error.h:20