CCF
Loading...
Searching...
No Matches
json_schema.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
5#include "ccf/ds/nonstd.h"
6
7#include <optional>
8#define FMT_HEADER_ONLY
9#include <fmt/format.h>
10#include <nlohmann/json.hpp>
11#include <set>
12#include <unordered_set>
13
15{
17 {
18 static constexpr auto hyperschema =
19 "http://json-schema.org/draft-07/schema#";
20
21 nlohmann::json schema;
22 };
23
24 inline void to_json(nlohmann::json& j, const JsonSchema& s)
25 {
26 j = s.schema;
27 }
28
29 inline void from_json(const nlohmann::json& j, JsonSchema& s)
30 {
31 s.schema = j;
32 }
33
34 template <typename T>
35 inline void fill_number_schema(nlohmann::json& schema)
36 {
37 schema["type"] = "integer";
38 schema["minimum"] = std::numeric_limits<T>::min();
39 schema["maximum"] = std::numeric_limits<T>::max();
40 }
41
42 template <typename T>
43 std::string schema_name();
44
45 template <typename T>
46 void fill_schema(nlohmann::json& schema);
47
48 template <typename T>
49 void fill_json_schema(nlohmann::json& j, const T* t);
50
51 template <typename T>
52 nlohmann::json schema_element()
53 {
54 auto element = nlohmann::json::object();
55 fill_schema<T>(element);
56 return element;
57 }
58
59 template <typename T, typename Doc>
60 nlohmann::json schema_element()
61 {
62 auto element = nlohmann::json::object();
63 fill_schema<T>(element);
64 return element;
65 }
66
67 namespace adl
68 {
69#pragma clang diagnostic push
70#if defined(__clang__) && __clang_major__ >= 11
71# pragma clang diagnostic ignored "-Wuninitialized-const-reference"
72#endif
73 template <typename T>
74 std::string schema_name()
75 {
76 T* t = nullptr;
77 return schema_name(t);
78 }
79
80 template <typename T>
81 void fill_schema(nlohmann::json& schema)
82 {
83 T* t = nullptr;
84 if constexpr (std::is_enum_v<T>)
85 {
86 fill_enum_schema(schema, t);
87 }
88 else
89 {
90 fill_json_schema(schema, t);
91 }
92 }
93 }
94#pragma clang diagnostic pop
95
96 template <typename T>
97 inline std::string schema_name()
98 {
100 {
101 return schema_name<typename T::value_type>();
102 }
104 {
105 if constexpr (std::is_same_v<T, std::vector<uint8_t>>)
106 {
107 // Byte vectors are always base64 encoded
108 return "base64string";
109 }
110 else
111 {
112 return fmt::format("{}_array", schema_name<typename T::value_type>());
113 }
114 }
115 else if constexpr (
118 {
119 return fmt::format("{}_set", schema_name<typename T::value_type>());
120 }
121 else if constexpr (
124 {
125 return fmt::format(
126 "{}_to_{}",
127 schema_name<typename T::key_type>(),
128 schema_name<typename T::mapped_type>());
129 }
131 {
132 return fmt::format(
133 "{}_and_{}",
134 schema_name<typename T::first_type>(),
135 schema_name<typename T::second_type>());
136 }
137 else if constexpr (std::is_same_v<T, std::string>)
138 {
139 return "string";
140 }
141 else if constexpr (std::is_same_v<T, bool>)
142 {
143 return "boolean";
144 }
145 else if constexpr (std::is_same_v<T, uint8_t>)
146 {
147 return "uint8";
148 }
149 else if constexpr (std::is_same_v<T, uint16_t>)
150 {
151 return "uint16";
152 }
153 else if constexpr (std::is_same_v<T, uint32_t>)
154 {
155 return "uint32";
156 }
157 else if constexpr (std::is_same_v<T, uint64_t>)
158 {
159 return "uint64";
160 }
161 else if constexpr (std::is_same_v<T, int8_t>)
162 {
163 return "int8";
164 }
165 else if constexpr (std::is_same_v<T, int16_t>)
166 {
167 return "int16";
168 }
169 else if constexpr (std::is_same_v<T, int32_t>)
170 {
171 return "int32";
172 }
173 else if constexpr (std::is_same_v<T, int64_t>)
174 {
175 return "int64";
176 }
177 else if constexpr (std::is_same_v<T, float>)
178 {
179 return "float";
180 }
181 else if constexpr (std::is_same_v<T, double>)
182 {
183 return "double";
184 }
185 else if constexpr (std::is_same_v<T, nlohmann::json>)
186 {
187 return "json";
188 }
189 else if constexpr (std::is_same_v<T, JsonSchema>)
190 {
191 return "json_schema";
192 }
193 else
194 {
195 return adl::schema_name<T>();
196 }
197 }
198
199 template <typename T>
200 inline void fill_schema(nlohmann::json& schema)
201 {
203 {
204 fill_schema<typename T::value_type>(schema);
205 }
206 else if constexpr (
210 {
211 if constexpr (std::is_same_v<T, std::vector<uint8_t>>)
212 {
213 // Byte vectors are always base64 encoded
214 schema["type"] = "string";
215 schema["format"] = "base64";
216 }
217 else
218 {
219 schema["type"] = "array";
220 schema["items"] = schema_element<typename T::value_type>();
221 }
222 }
223 else if constexpr (
226 {
227 // Nlohmann JSON serialises some maps as objects, if the keys can be
228 // converted to strings. This should detect those cases. The others are
229 // serialised as list-of-pairs
230 if constexpr (nlohmann::detail::
231 is_compatible_object_type<nlohmann::json, T>::value)
232 {
233 schema["type"] = "object";
234 schema["additionalProperties"] =
235 schema_element<typename T::mapped_type>();
236 }
237 else
238 {
239 schema["type"] = "array";
240 auto items = nlohmann::json::object();
241 {
242 items["type"] = "array";
243
244 auto sub_items = nlohmann::json::array();
245 sub_items.push_back(schema_element<typename T::key_type>());
246 sub_items.push_back(schema_element<typename T::mapped_type>());
247 items["items"] = sub_items;
248 }
249 schema["items"] = items;
250 }
251 }
253 {
254 schema["type"] = "array";
255 auto items = nlohmann::json::array();
256 items.push_back(schema_element<typename T::first_type>());
257 items.push_back(schema_element<typename T::second_type>());
258 schema["items"] = items;
259 }
260 else if constexpr (std::is_same_v<T, std::string>)
261 {
262 schema["type"] = "string";
263 }
264 else if constexpr (std::is_same_v<T, bool>)
265 {
266 schema["type"] = "boolean";
267 }
268 else if constexpr (std::is_same_v<T, nlohmann::json>)
269 {
270 // Any field that contains more json is completely unconstrained, so we
271 // do not add a type or any other fields
272 schema = nlohmann::json::object();
273 }
274 else if constexpr (std::is_integral_v<T>)
275 {
276 fill_number_schema<T>(schema);
277 }
278 else if constexpr (std::is_floating_point_v<T>)
279 {
280 schema["type"] = "number";
281 }
282 else if constexpr (std::is_same_v<T, JsonSchema>)
283 {
284 schema["type"] = "object";
285 }
286 else
287 {
288 adl::fill_schema<T>(schema);
289 }
290 }
291
292 template <typename T>
293 inline nlohmann::json build_schema(const std::string& title = "")
294 {
295 auto schema = nlohmann::json::object();
296
297 if (!title.empty())
298 {
299 schema["title"] = title;
300 }
301
302 fill_schema<T>(schema);
303
304 return schema;
305 }
306}
void fill_schema(nlohmann::json &schema)
Definition json_schema.h:81
std::string schema_name()
Definition json_schema.h:74
Definition json_schema.h:15
void fill_json_schema(nlohmann::json &j, const T *t)
void fill_schema(nlohmann::json &schema)
Definition json_schema.h:200
std::string schema_name()
Definition json_schema.h:97
void fill_number_schema(nlohmann::json &schema)
Definition json_schema.h:35
void to_json(nlohmann::json &j, const JsonSchema &s)
Definition json_schema.h:24
nlohmann::json schema_element()
Definition json_schema.h:52
nlohmann::json build_schema(const std::string &title="")
Definition json_schema.h:293
void from_json(const nlohmann::json &j, JsonSchema &s)
Definition json_schema.h:29
Definition json_schema.h:17
static constexpr auto hyperschema
Definition json_schema.h:18
nlohmann::json schema
Definition json_schema.h:21
Definition nonstd.h:30