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