DAW JSON Link
daw_json_schema.h
Go to the documentation of this file.
1 // Copyright (c) Darrell Wright
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
5 //
6 // Official repository: https://github.com/beached/daw_json_link
7 //
8 
9 #pragma once
10 
11 #include "daw_json_link_types.h"
13 
14 namespace daw::json {
15  inline namespace DAW_JSON_VER {
16  namespace json_details {
17 
18  template<typename JsonMember, bool is_root = false,
19  typename OutputIterator>
20  constexpr OutputIterator to_json_schema( ParseTag<JsonParseTypes::Bool>,
21  OutputIterator out_it ) {
22  if constexpr( not is_root ) {
23  *out_it++ = '{';
24  }
25  out_it = utils::copy_to_iterator( out_it, R"("type":"boolean")" );
26  if constexpr( not is_root ) {
27  *out_it++ = '}';
28  }
29  return out_it;
30  }
31 
32  template<typename JsonMember, bool is_root = false,
33  typename OutputIterator>
34  constexpr OutputIterator to_json_schema( ParseTag<JsonParseTypes::Custom>,
35  OutputIterator out_it ) {
36  // TODO allow a trait to describe the valid literal types or if it
37  // matches one of the other predefined types
38  static_assert( JsonMember::custom_json_type ==
40  if constexpr( not is_root ) {
41  *out_it++ = '{';
42  }
43  out_it = utils::copy_to_iterator( out_it, R"("type":"string")" );
44  if constexpr( not is_root ) {
45  *out_it++ = '}';
46  }
47  return out_it;
48  }
49 
50  template<typename JsonMember, bool is_root = false,
51  typename OutputIterator>
52  constexpr OutputIterator to_json_schema( ParseTag<JsonParseTypes::Date>,
53  OutputIterator out_it ) {
54 
55  if constexpr( not is_root ) {
56  *out_it++ = '{';
57  }
58  out_it = utils::copy_to_iterator(
59  out_it, R"("type":"string","format":"date-time")" );
60  if constexpr( not is_root ) {
61  *out_it++ = '}';
62  }
63  return out_it;
64  }
65 
66  template<typename JsonMember, bool is_root = false,
67  typename OutputIterator>
68  constexpr OutputIterator to_json_schema( ParseTag<JsonParseTypes::Real>,
69  OutputIterator out_it ) {
70 
71  if constexpr( not is_root ) {
72  *out_it++ = '{';
73  }
74  out_it = utils::copy_to_iterator( out_it, R"("type":"number")" );
75  if constexpr( not is_root ) {
76  *out_it++ = '}';
77  }
78  return out_it;
79  }
80 
81  template<typename JsonMember, bool is_root = false,
82  typename OutputIterator>
83  constexpr OutputIterator to_json_schema( ParseTag<JsonParseTypes::Signed>,
84  OutputIterator out_it ) {
85 
86  if constexpr( not is_root ) {
87  *out_it++ = '{';
88  }
89  out_it = utils::copy_to_iterator( out_it, R"("type":"integer")" );
90  if constexpr( not is_root ) {
91  *out_it++ = '}';
92  }
93  return out_it;
94  }
95 
96  template<typename JsonMember, bool is_root = false,
97  typename OutputIterator>
98  constexpr OutputIterator
99  to_json_schema( ParseTag<JsonParseTypes::StringEscaped>,
100  OutputIterator out_it ) {
101 
102  if constexpr( not is_root ) {
103  *out_it++ = '{';
104  }
105  out_it = utils::copy_to_iterator( out_it, R"("type":"string")" );
106  if constexpr( not is_root ) {
107  *out_it++ = '}';
108  }
109  return out_it;
110  }
111 
112  template<typename JsonMember, bool is_root = false,
113  typename OutputIterator>
114  constexpr OutputIterator
115  to_json_schema( ParseTag<JsonParseTypes::StringRaw>,
116  OutputIterator out_it ) {
117 
118  if constexpr( not is_root ) {
119  *out_it++ = '{';
120  }
121  out_it = utils::copy_to_iterator( out_it, R"("type":"string")" );
122  if constexpr( not is_root ) {
123  *out_it++ = '}';
124  }
125  return out_it;
126  }
127 
128  template<typename JsonMember, bool is_root = false,
129  typename OutputIterator>
130  constexpr OutputIterator
131  to_json_schema( ParseTag<JsonParseTypes::Unsigned>,
132  OutputIterator out_it ) {
133  if constexpr( not is_root ) {
134  *out_it++ = '{';
135  }
136  out_it =
137  utils::copy_to_iterator( out_it, R"("type":"integer","minimum":0)" );
138  if constexpr( not is_root ) {
139  *out_it++ = '}';
140  }
141  return out_it;
142  }
143 
144  /***
145  * Output the schema of a json_class and it's members
146  * @tparam JsonMember A json_class type
147  * @tparam is_root Is this the root item in the schema.
148  * @tparam OutputIterator An iterator type that allows for assigning to
149  * the result of operator* and pre/post-fix incrementing
150  * @param out_it Current OutputIterator
151  * @return the last value of out_it
152  */
153  template<typename JsonMember, bool is_root = false,
154  typename OutputIterator>
155  constexpr OutputIterator to_json_schema( ParseTag<JsonParseTypes::Class>,
156  OutputIterator out_it );
157 
158  /***
159  * Output the schema of a json_array and it's element type
160  * @tparam JsonMember A json_array type
161  * @tparam is_root Is this the root item in the schema.
162  * @tparam OutputIterator An iterator type that allows for assigning to
163  * the result of operator and pre/post-fix incrementing
164  * @param out_it Current OutputIterator
165  * @return the last value of out_it
166  */
167  template<typename JsonMember, bool is_root = false,
168  typename OutputIterator>
169  constexpr OutputIterator to_json_schema( ParseTag<JsonParseTypes::Array>,
170  OutputIterator out_it );
171 
172  template<typename JsonMember, bool is_root = false,
173  typename OutputIterator>
174  constexpr OutputIterator to_json_schema( ParseTag<JsonParseTypes::Tuple>,
175  OutputIterator out_it );
176 
177  template<typename JsonMember, bool is_root = false,
178  typename OutputIterator>
179  constexpr OutputIterator
180  to_json_schema( ParseTag<JsonParseTypes::SizedArray>,
181  OutputIterator out_it );
182 
183  template<typename JsonMember, bool is_root = false,
184  typename OutputIterator>
185  constexpr OutputIterator
186  to_json_schema( ParseTag<JsonParseTypes::KeyValue>,
187  OutputIterator out_it );
188 
189  template<typename JsonMember, bool is_root = false,
190  typename OutputIterator>
191  constexpr OutputIterator
192  to_json_schema( ParseTag<JsonParseTypes::KeyValueArray>,
193  OutputIterator out_it );
194 
195  template<typename JsonMember, bool is_root = false,
196  typename OutputIterator>
197  constexpr OutputIterator
198  to_json_schema( ParseTag<JsonParseTypes::Variant>,
199  OutputIterator out_it );
200 
201  template<typename JsonMember, bool is_root = false,
202  typename OutputIterator>
203  constexpr OutputIterator
204  to_json_schema( ParseTag<JsonParseTypes::VariantTagged>,
205  OutputIterator out_it );
206 
207  template<typename, typename>
208  struct json_class_processor;
209 
210  template<typename OutputIterator, typename... JsonMembers>
211  struct json_class_processor<OutputIterator,
212  json_member_list<JsonMembers...>> {
213 
214  static constexpr OutputIterator process( OutputIterator out_it ) {
215  out_it = utils::copy_to_iterator(
216  out_it, R"("type":"object","properties":{)" );
217  out_it = output_member_types(
218  out_it, std::index_sequence_for<JsonMembers...>{ } );
219  out_it = utils::copy_to_iterator( out_it, R"(},"required":[)" );
220  out_it = output_required_members( out_it );
221  *out_it++ = ']';
222  if constexpr( ( has_dependent_member_v<JsonMembers> or ... ) ) {
223  out_it = utils::copy_to_iterator( out_it, R"(,"dependencies":{)" );
224  bool is_first = true;
225  out_it = static_cast<OutputIterator>(
226  ( output_dependency<
227  json_link_no_name<json_link_no_name<JsonMembers>>>(
228  out_it, is_first ),
229  ... ) );
230  *out_it++ = '}';
231  }
232  return out_it;
233  }
234 
235  private:
236  static constexpr auto indexer =
237  std::index_sequence_for<JsonMembers...>{ };
238 
239  template<typename JsonMember, std::size_t Idx>
240  static constexpr OutputIterator
241  output_member_type( OutputIterator &out_it, bool &is_first,
242  bool *seen ) {
243  if( seen[Idx] ) {
244  return out_it;
245  }
246  seen[Idx] = true;
247  if( not is_first ) {
248  *out_it++ = ',';
249  } else {
250  is_first = false;
251  }
252  *out_it++ = '"';
253  out_it = utils::copy_to_iterator( out_it, JsonMember::name );
254  out_it = utils::copy_to_iterator( out_it, R"(":)" );
255  out_it = to_json_schema<JsonMember>(
256  ParseTag<JsonMember::base_expected_type>{ }, out_it );
257  return out_it;
258  }
259 
260  template<std::size_t... Is>
261  static constexpr OutputIterator
262  output_member_types( OutputIterator &out_it,
263  std::index_sequence<Is...> ) {
264  bool is_first = true;
265  bool seen[sizeof...( JsonMembers )]{ };
266  return static_cast<OutputIterator>(
267  ( output_member_type<JsonMembers, Is>( out_it, is_first, seen ),
268  ... ) );
269  }
270 
271  template<typename JsonMember>
272  static constexpr OutputIterator
273  output_required_member( OutputIterator &out_it, bool &is_first ) {
274  if constexpr( JsonMember::nullable == JsonNullable::MustExist ) {
275  if( not is_first ) {
276  *out_it++ = ',';
277  } else {
278  is_first = false;
279  }
280  *out_it++ = '"';
281  out_it = utils::copy_to_iterator( out_it, JsonMember::name );
282  *out_it++ = '"';
283  }
284  return out_it;
285  }
286 
287  template<typename JsonMember>
288  static constexpr OutputIterator
289  output_dependency( OutputIterator &out_it, bool &is_first ) {
290  if constexpr( has_dependent_member_v<JsonMember> ) {
291  if( not is_first ) {
292  *out_it++ = ',';
293  } else {
294  is_first = false;
295  }
296  *out_it++ = '"';
297  out_it = utils::copy_to_iterator( out_it, JsonMember::name );
298  out_it = utils::copy_to_iterator( out_it, R"(":[")" );
299  out_it = utils::copy_to_iterator(
300  out_it, dependent_member_t<JsonMember>::name );
301  out_it = utils::copy_to_iterator( out_it, R"("])" );
302  }
303  return out_it;
304  }
305 
306  static constexpr OutputIterator
307  output_required_members( OutputIterator &out_it ) {
308  bool is_first = true;
309  return ( output_required_member<json_link_no_name<JsonMembers>>(
310  out_it, is_first ),
311  ... );
312  }
313  };
314 
315  template<typename OutputIterator, typename... JsonMembers>
316  struct json_class_processor<OutputIterator,
317  json_ordered_member_list<JsonMembers...>> {
318 
319  static constexpr OutputIterator process( OutputIterator out_it ) {
320 
321  out_it =
322  utils::copy_to_iterator( out_it, R"("type":"array","items":[)" );
323  out_it = output_member_types( out_it );
324  *out_it++ = ']';
325 
326  static_assert(
327  not( ( json_link_no_name<JsonMembers>::base_expected_type ==
329  ... ),
330  "A tagged variant is not supported in a tuple/ordered json class" );
331  return out_it;
332  }
333 
334  static constexpr OutputIterator
335  output_member_types( OutputIterator &out_it ) {
336  bool is_first = true;
337  return static_cast<OutputIterator>(
338  ( output_member_type<json_link_no_name<JsonMembers>>( out_it,
339  is_first ),
340  ... ) );
341  }
342 
343  template<typename JsonMember>
344  static constexpr OutputIterator
345  output_member_type( OutputIterator &out_it, bool &is_first ) {
346  if( not is_first ) {
347  *out_it++ = ',';
348  } else {
349  is_first = false;
350  }
351  out_it = to_json_schema<JsonMember>(
352  ParseTag<JsonMember::base_expected_type>{ }, out_it );
353  return out_it;
354  }
355  };
356 
357  template<typename JsonMember, bool is_root, typename OutputIterator>
358  constexpr OutputIterator to_json_schema( ParseTag<JsonParseTypes::Class>,
359  OutputIterator out_it ) {
360  if constexpr( not is_root ) {
361  *out_it++ = '{';
362  }
363 
364  using json_class_processor_t = json_class_processor<
365  OutputIterator,
366  json_data_contract_trait_t<typename JsonMember::base_type>>;
367 
368  out_it = json_class_processor_t::process( out_it );
369 
370  if constexpr( not is_root ) {
371  *out_it++ = '}';
372  }
373  return out_it;
374  }
375 
376  namespace json_details {
377  template<typename Tuple, bool is_root, typename OutputIterator,
378  std::size_t... Is>
379  constexpr OutputIterator
380  to_json_tuple_schema( OutputIterator out_it,
381  std::index_sequence<Is...> ) {
382  if constexpr( not is_root ) {
383  *out_it++ = '{';
384  }
385 
386  bool is_first = true;
387  auto const process_member = [&]( auto Idx ) {
388  if( not is_first ) {
389  *out_it++ = ',';
390  } else {
391  is_first = false;
392  }
393  static constexpr std::size_t index = decltype( Idx )::value;
394  using pack_element = tuple_elements_pack<Tuple>;
395  using JsonMember = json_deduced_type<
396  typename pack_element::template element_t<index>>;
397 
398  out_it = to_json_schema<JsonMember>(
399  ParseTag<JsonMember::base_expected_type>{ }, out_it );
400  };
401 
402  daw::Empty expander[] = {
403  ( process_member( std::integral_constant<std::size_t, Is>{ } ),
404  daw::Empty{ } )...,
405  daw::Empty{} };
406  (void)expander;
407 
408  if constexpr( not is_root ) {
409  *out_it++ = '}';
410  }
411  return out_it;
412  }
413  } // namespace json_details
414 
415  template<typename JsonMember, bool is_root, typename OutputIterator>
416  constexpr OutputIterator to_json_schema( ParseTag<JsonParseTypes::Tuple>,
417  OutputIterator out_it ) {
418  using tuple_t = typename JsonMember::base_type;
419  return json_details::to_json_tuple_schema<tuple_t, is_root>(
420  out_it,
421  std::make_index_sequence<tuple_elements_pack<tuple_t>::size>{ } );
422  }
423 
424  template<typename JsonMember, bool is_root, typename OutputIterator>
425  constexpr OutputIterator to_json_schema( ParseTag<JsonParseTypes::Array>,
426  OutputIterator out_it ) {
427  if constexpr( not is_root ) {
428  *out_it++ = '{';
429  }
430  out_it =
431  utils::copy_to_iterator( out_it, R"("type":"array","items":)" );
432  using element_t = typename JsonMember::json_element_t;
433  out_it = to_json_schema<element_t>(
434  ParseTag<element_t::base_expected_type>{ }, out_it );
435  if constexpr( not is_root ) {
436  *out_it++ = '}';
437  }
438  return out_it;
439  }
440 
441  template<typename JsonMember, bool is_root, typename OutputIterator>
442  constexpr OutputIterator
443  to_json_schema( ParseTag<JsonParseTypes::SizedArray>,
444  OutputIterator out_it ) {
445  return to_json_schema<JsonMember, is_root>(
446  ParseTag<JsonParseTypes::Array>{ }, out_it );
447  }
448 
449  template<typename JsonMember, bool is_root, typename OutputIterator>
450  constexpr OutputIterator
451  to_json_schema( ParseTag<JsonParseTypes::KeyValue>,
452  OutputIterator out_it ) {
453  if constexpr( not is_root ) {
454  *out_it++ = '{';
455  }
456  out_it = utils::copy_to_iterator(
457  out_it, R"("type":"object","additionalProperties":)" );
458  using element_t = typename JsonMember::json_element_t;
459  out_it = to_json_schema<element_t>(
460  ParseTag<element_t::base_expected_type>{ }, out_it );
461  if constexpr( not is_root ) {
462  *out_it++ = '}';
463  }
464  return out_it;
465  }
466 
467  template<typename JsonMember, bool is_root, typename OutputIterator>
468  constexpr OutputIterator
469  to_json_schema( ParseTag<JsonParseTypes::KeyValueArray>,
470  OutputIterator out_it ) {
471  if constexpr( not is_root ) {
472  *out_it++ = '{';
473  }
474  out_it =
475  utils::copy_to_iterator( out_it, R"("type":"array","items":)" );
476  using element_t = typename JsonMember::json_class_t;
477  out_it = to_json_schema<element_t>(
478  ParseTag<element_t::base_expected_type>{ }, out_it );
479  if constexpr( not is_root ) {
480  *out_it++ = '}';
481  }
482  return out_it;
483  }
484 
485  template<typename...>
486  struct variant_element_types;
487 
488  template<typename... JsonElements>
489  struct variant_element_types<json_variant_type_list<JsonElements...>> {
490 
491  template<typename JsonElement, typename OutputIterator>
492  static constexpr OutputIterator output_element( OutputIterator out_it,
493  bool &is_first ) {
494  if( not is_first ) {
495  *out_it++ = ',';
496  } else {
497  is_first = false;
498  }
499  return to_json_schema<JsonElement>(
500  ParseTag<JsonElement::base_expected_type>{ }, out_it );
501  }
502 
503  template<typename OutputIterator>
504  static constexpr OutputIterator
505  output_elements( OutputIterator out_it ) {
506  bool is_first = true;
507  return static_cast<OutputIterator>(
508  ( output_element<JsonElements>( out_it, is_first ), ... ) );
509  }
510  };
511 
512  template<typename JsonMember, bool is_root, typename OutputIterator>
513  constexpr OutputIterator
514  to_json_schema( ParseTag<JsonParseTypes::Variant>,
515  OutputIterator out_it ) {
516  if constexpr( not is_root ) {
517  *out_it++ = '{';
518  }
519  out_it = utils::copy_to_iterator( out_it, R"("oneOf":[)" );
520  using elements_t = typename JsonMember::json_elements;
521  out_it = variant_element_types<elements_t>::output_elements( out_it );
522  *out_it++ = ']';
523  if constexpr( not is_root ) {
524  *out_it++ = '}';
525  }
526  return out_it;
527  }
528 
529  template<typename JsonMember, bool is_root, typename OutputIterator>
530  constexpr OutputIterator
531  to_json_schema( ParseTag<JsonParseTypes::VariantTagged>,
532  OutputIterator out_it ) {
533  static_assert( not is_root,
534  "Attempt to have a tagged variant as root object. This "
535  "is unsupported" );
536  *out_it++ = '{';
537  out_it = utils::copy_to_iterator( out_it, R"("oneOf":[)" );
538  using elements_t = typename JsonMember::json_elements;
539  out_it = variant_element_types<elements_t>::output_elements( out_it );
540  *out_it++ = ']';
541  *out_it++ = '}';
542  return out_it;
543  }
544 
545  } // namespace json_details
546 
547  template<typename T, typename OutputIterator>
548  constexpr OutputIterator to_json_schema( OutputIterator out_it,
549  std::string_view id,
550  std::string_view title ) {
551  *out_it++ = '{';
552  out_it = utils::copy_to_iterator(
553  out_it,
554  R"("$schema":"https://json-schema.org/draft/2020-12/schema",)" );
555  out_it = utils::copy_to_iterator( out_it, R"("$id":")" );
556  out_it = utils::copy_to_iterator( out_it, id );
557  out_it = utils::copy_to_iterator( out_it, R"(","title":")" );
558  out_it = utils::copy_to_iterator( out_it, title );
559  out_it = utils::copy_to_iterator( out_it, R"(",)" );
560 
561  using json_type = json_link_no_name<T>;
562  out_it = json_details::to_json_schema<json_type, true>(
563  ParseTag<json_type::base_expected_type>{ }, out_it );
564  *out_it++ = '}';
565  return out_it;
566  }
567 
568  template<typename T>
569  std::string to_json_schema( std::string_view id, std::string_view title ) {
570  auto result = std::string( );
571  (void)to_json_schema<T>( std::back_inserter( result ), id, title );
572  return result;
573  }
574  } // namespace DAW_JSON_VER
575 } // namespace daw::json
typename json_type_deducer< T, has_json_data_contract_trait_v< T >, json_details::is_a_json_type_v< T >, has_json_link_quick_map_v< T >, is_container_v< T > >::type json_deduced_type
Definition: daw_json_parse_common.h:931
constexpr OutputIterator copy_to_iterator(OutputIterator it, Container const &container)
Definition: to_daw_json_string.h:398
Definition: daw_from_json.h:22
#define DAW_JSON_VER
Definition: version.h:11