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