xref: /aosp_15_r20/external/flatbuffers/src/idl_gen_json_schema.cpp (revision 890232f25432b36107d06881e0a25aaa6b473652)
1*890232f2SAndroid Build Coastguard Worker /*
2*890232f2SAndroid Build Coastguard Worker  * Copyright 2014 Google Inc. All rights reserved.
3*890232f2SAndroid Build Coastguard Worker  *
4*890232f2SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*890232f2SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*890232f2SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*890232f2SAndroid Build Coastguard Worker  *
8*890232f2SAndroid Build Coastguard Worker  *     http://www.apache.org/licenses/LICENSE-2.0
9*890232f2SAndroid Build Coastguard Worker  *
10*890232f2SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*890232f2SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*890232f2SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*890232f2SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*890232f2SAndroid Build Coastguard Worker  * limitations under the License.
15*890232f2SAndroid Build Coastguard Worker  */
16*890232f2SAndroid Build Coastguard Worker 
17*890232f2SAndroid Build Coastguard Worker #include <iostream>
18*890232f2SAndroid Build Coastguard Worker 
19*890232f2SAndroid Build Coastguard Worker #include "flatbuffers/code_generators.h"
20*890232f2SAndroid Build Coastguard Worker #include "flatbuffers/idl.h"
21*890232f2SAndroid Build Coastguard Worker #include "flatbuffers/util.h"
22*890232f2SAndroid Build Coastguard Worker 
23*890232f2SAndroid Build Coastguard Worker namespace flatbuffers {
24*890232f2SAndroid Build Coastguard Worker 
25*890232f2SAndroid Build Coastguard Worker namespace jsons {
26*890232f2SAndroid Build Coastguard Worker 
27*890232f2SAndroid Build Coastguard Worker namespace {
28*890232f2SAndroid Build Coastguard Worker 
29*890232f2SAndroid Build Coastguard Worker template<class T>
GenFullName(const T * enum_def)30*890232f2SAndroid Build Coastguard Worker static std::string GenFullName(const T *enum_def) {
31*890232f2SAndroid Build Coastguard Worker   std::string full_name;
32*890232f2SAndroid Build Coastguard Worker   const auto &name_spaces = enum_def->defined_namespace->components;
33*890232f2SAndroid Build Coastguard Worker   for (auto ns = name_spaces.cbegin(); ns != name_spaces.cend(); ++ns) {
34*890232f2SAndroid Build Coastguard Worker     full_name.append(*ns + "_");
35*890232f2SAndroid Build Coastguard Worker   }
36*890232f2SAndroid Build Coastguard Worker   full_name.append(enum_def->name);
37*890232f2SAndroid Build Coastguard Worker   return full_name;
38*890232f2SAndroid Build Coastguard Worker }
39*890232f2SAndroid Build Coastguard Worker 
40*890232f2SAndroid Build Coastguard Worker template<class T>
GenTypeRef(const T * enum_def)41*890232f2SAndroid Build Coastguard Worker static std::string GenTypeRef(const T *enum_def) {
42*890232f2SAndroid Build Coastguard Worker   return "\"$ref\" : \"#/definitions/" + GenFullName(enum_def) + "\"";
43*890232f2SAndroid Build Coastguard Worker }
44*890232f2SAndroid Build Coastguard Worker 
GenType(const std::string & name)45*890232f2SAndroid Build Coastguard Worker static std::string GenType(const std::string &name) {
46*890232f2SAndroid Build Coastguard Worker   return "\"type\" : \"" + name + "\"";
47*890232f2SAndroid Build Coastguard Worker }
48*890232f2SAndroid Build Coastguard Worker 
GenType(BaseType type)49*890232f2SAndroid Build Coastguard Worker static std::string GenType(BaseType type) {
50*890232f2SAndroid Build Coastguard Worker   switch (type) {
51*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_BOOL: return "\"type\" : \"boolean\"";
52*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_CHAR:
53*890232f2SAndroid Build Coastguard Worker       return "\"type\" : \"integer\", \"minimum\" : " +
54*890232f2SAndroid Build Coastguard Worker              NumToString(std::numeric_limits<int8_t>::min()) +
55*890232f2SAndroid Build Coastguard Worker              ", \"maximum\" : " +
56*890232f2SAndroid Build Coastguard Worker              NumToString(std::numeric_limits<int8_t>::max());
57*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_UCHAR:
58*890232f2SAndroid Build Coastguard Worker       return "\"type\" : \"integer\", \"minimum\" : 0, \"maximum\" :" +
59*890232f2SAndroid Build Coastguard Worker              NumToString(std::numeric_limits<uint8_t>::max());
60*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_SHORT:
61*890232f2SAndroid Build Coastguard Worker       return "\"type\" : \"integer\", \"minimum\" : " +
62*890232f2SAndroid Build Coastguard Worker              NumToString(std::numeric_limits<int16_t>::min()) +
63*890232f2SAndroid Build Coastguard Worker              ", \"maximum\" : " +
64*890232f2SAndroid Build Coastguard Worker              NumToString(std::numeric_limits<int16_t>::max());
65*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_USHORT:
66*890232f2SAndroid Build Coastguard Worker       return "\"type\" : \"integer\", \"minimum\" : 0, \"maximum\" : " +
67*890232f2SAndroid Build Coastguard Worker              NumToString(std::numeric_limits<uint16_t>::max());
68*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_INT:
69*890232f2SAndroid Build Coastguard Worker       return "\"type\" : \"integer\", \"minimum\" : " +
70*890232f2SAndroid Build Coastguard Worker              NumToString(std::numeric_limits<int32_t>::min()) +
71*890232f2SAndroid Build Coastguard Worker              ", \"maximum\" : " +
72*890232f2SAndroid Build Coastguard Worker              NumToString(std::numeric_limits<int32_t>::max());
73*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_UINT:
74*890232f2SAndroid Build Coastguard Worker       return "\"type\" : \"integer\", \"minimum\" : 0, \"maximum\" : " +
75*890232f2SAndroid Build Coastguard Worker              NumToString(std::numeric_limits<uint32_t>::max());
76*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_LONG:
77*890232f2SAndroid Build Coastguard Worker       return "\"type\" : \"integer\", \"minimum\" : " +
78*890232f2SAndroid Build Coastguard Worker              NumToString(std::numeric_limits<int64_t>::min()) +
79*890232f2SAndroid Build Coastguard Worker              ", \"maximum\" : " +
80*890232f2SAndroid Build Coastguard Worker              NumToString(std::numeric_limits<int64_t>::max());
81*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_ULONG:
82*890232f2SAndroid Build Coastguard Worker       return "\"type\" : \"integer\", \"minimum\" : 0, \"maximum\" : " +
83*890232f2SAndroid Build Coastguard Worker              NumToString(std::numeric_limits<uint64_t>::max());
84*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_FLOAT:
85*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_DOUBLE: return "\"type\" : \"number\"";
86*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_STRING: return "\"type\" : \"string\"";
87*890232f2SAndroid Build Coastguard Worker     default: return "";
88*890232f2SAndroid Build Coastguard Worker   }
89*890232f2SAndroid Build Coastguard Worker }
90*890232f2SAndroid Build Coastguard Worker 
GenBaseType(const Type & type)91*890232f2SAndroid Build Coastguard Worker static std::string GenBaseType(const Type &type) {
92*890232f2SAndroid Build Coastguard Worker   if (type.struct_def != nullptr) { return GenTypeRef(type.struct_def); }
93*890232f2SAndroid Build Coastguard Worker   if (type.enum_def != nullptr) { return GenTypeRef(type.enum_def); }
94*890232f2SAndroid Build Coastguard Worker   return GenType(type.base_type);
95*890232f2SAndroid Build Coastguard Worker }
96*890232f2SAndroid Build Coastguard Worker 
GenArrayType(const Type & type)97*890232f2SAndroid Build Coastguard Worker static std::string GenArrayType(const Type &type) {
98*890232f2SAndroid Build Coastguard Worker   std::string element_type;
99*890232f2SAndroid Build Coastguard Worker   if (type.struct_def != nullptr) {
100*890232f2SAndroid Build Coastguard Worker     element_type = GenTypeRef(type.struct_def);
101*890232f2SAndroid Build Coastguard Worker   } else if (type.enum_def != nullptr) {
102*890232f2SAndroid Build Coastguard Worker     element_type = GenTypeRef(type.enum_def);
103*890232f2SAndroid Build Coastguard Worker   } else {
104*890232f2SAndroid Build Coastguard Worker     element_type = GenType(type.element);
105*890232f2SAndroid Build Coastguard Worker   }
106*890232f2SAndroid Build Coastguard Worker 
107*890232f2SAndroid Build Coastguard Worker   return "\"type\" : \"array\", \"items\" : {" + element_type + "}";
108*890232f2SAndroid Build Coastguard Worker }
109*890232f2SAndroid Build Coastguard Worker 
GenType(const Type & type)110*890232f2SAndroid Build Coastguard Worker static std::string GenType(const Type &type) {
111*890232f2SAndroid Build Coastguard Worker   switch (type.base_type) {
112*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH();  // fall thru
113*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_VECTOR: {
114*890232f2SAndroid Build Coastguard Worker       return GenArrayType(type);
115*890232f2SAndroid Build Coastguard Worker     }
116*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_STRUCT: {
117*890232f2SAndroid Build Coastguard Worker       return GenTypeRef(type.struct_def);
118*890232f2SAndroid Build Coastguard Worker     }
119*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_UNION: {
120*890232f2SAndroid Build Coastguard Worker       std::string union_type_string("\"anyOf\": [");
121*890232f2SAndroid Build Coastguard Worker       const auto &union_types = type.enum_def->Vals();
122*890232f2SAndroid Build Coastguard Worker       for (auto ut = union_types.cbegin(); ut < union_types.cend(); ++ut) {
123*890232f2SAndroid Build Coastguard Worker         const auto &union_type = *ut;
124*890232f2SAndroid Build Coastguard Worker         if (union_type->union_type.base_type == BASE_TYPE_NONE) { continue; }
125*890232f2SAndroid Build Coastguard Worker         if (union_type->union_type.base_type == BASE_TYPE_STRUCT) {
126*890232f2SAndroid Build Coastguard Worker           union_type_string.append(
127*890232f2SAndroid Build Coastguard Worker               "{ " + GenTypeRef(union_type->union_type.struct_def) + " }");
128*890232f2SAndroid Build Coastguard Worker         }
129*890232f2SAndroid Build Coastguard Worker         if (union_type != *type.enum_def->Vals().rbegin()) {
130*890232f2SAndroid Build Coastguard Worker           union_type_string.append(",");
131*890232f2SAndroid Build Coastguard Worker         }
132*890232f2SAndroid Build Coastguard Worker       }
133*890232f2SAndroid Build Coastguard Worker       union_type_string.append("]");
134*890232f2SAndroid Build Coastguard Worker       return union_type_string;
135*890232f2SAndroid Build Coastguard Worker     }
136*890232f2SAndroid Build Coastguard Worker     case BASE_TYPE_UTYPE: return GenTypeRef(type.enum_def);
137*890232f2SAndroid Build Coastguard Worker     default: {
138*890232f2SAndroid Build Coastguard Worker       return GenBaseType(type);
139*890232f2SAndroid Build Coastguard Worker     }
140*890232f2SAndroid Build Coastguard Worker   }
141*890232f2SAndroid Build Coastguard Worker }
142*890232f2SAndroid Build Coastguard Worker 
143*890232f2SAndroid Build Coastguard Worker } // namespace
144*890232f2SAndroid Build Coastguard Worker 
145*890232f2SAndroid Build Coastguard Worker class JsonSchemaGenerator : public BaseGenerator {
146*890232f2SAndroid Build Coastguard Worker  private:
147*890232f2SAndroid Build Coastguard Worker   std::string code_;
148*890232f2SAndroid Build Coastguard Worker 
149*890232f2SAndroid Build Coastguard Worker  public:
JsonSchemaGenerator(const Parser & parser,const std::string & path,const std::string & file_name)150*890232f2SAndroid Build Coastguard Worker   JsonSchemaGenerator(const Parser &parser, const std::string &path,
151*890232f2SAndroid Build Coastguard Worker                       const std::string &file_name)
152*890232f2SAndroid Build Coastguard Worker       : BaseGenerator(parser, path, file_name, "", "", "json") {}
153*890232f2SAndroid Build Coastguard Worker 
JsonSchemaGenerator(const BaseGenerator & base_generator)154*890232f2SAndroid Build Coastguard Worker   explicit JsonSchemaGenerator(const BaseGenerator &base_generator)
155*890232f2SAndroid Build Coastguard Worker       : BaseGenerator(base_generator) {}
156*890232f2SAndroid Build Coastguard Worker 
GeneratedFileName(const std::string & path,const std::string & file_name,const IDLOptions & options) const157*890232f2SAndroid Build Coastguard Worker   std::string GeneratedFileName(const std::string &path,
158*890232f2SAndroid Build Coastguard Worker                                 const std::string &file_name,
159*890232f2SAndroid Build Coastguard Worker                                 const IDLOptions &options /* unused */) const {
160*890232f2SAndroid Build Coastguard Worker     (void)options;
161*890232f2SAndroid Build Coastguard Worker     return path + file_name + ".schema.json";
162*890232f2SAndroid Build Coastguard Worker   }
163*890232f2SAndroid Build Coastguard Worker 
164*890232f2SAndroid Build Coastguard Worker   // If indentation is less than 0, that indicates we don't want any newlines
165*890232f2SAndroid Build Coastguard Worker   // either.
NewLine() const166*890232f2SAndroid Build Coastguard Worker   std::string NewLine() const {
167*890232f2SAndroid Build Coastguard Worker     return parser_.opts.indent_step >= 0 ? "\n" : "";
168*890232f2SAndroid Build Coastguard Worker   }
169*890232f2SAndroid Build Coastguard Worker 
Indent(int indent) const170*890232f2SAndroid Build Coastguard Worker   std::string Indent(int indent) const {
171*890232f2SAndroid Build Coastguard Worker     const auto num_spaces = indent * std::max(parser_.opts.indent_step, 0);
172*890232f2SAndroid Build Coastguard Worker     return std::string(num_spaces, ' ');
173*890232f2SAndroid Build Coastguard Worker   }
174*890232f2SAndroid Build Coastguard Worker 
PrepareDescription(const std::vector<std::string> & comment_lines)175*890232f2SAndroid Build Coastguard Worker   std::string PrepareDescription(
176*890232f2SAndroid Build Coastguard Worker       const std::vector<std::string> &comment_lines) {
177*890232f2SAndroid Build Coastguard Worker     std::string comment;
178*890232f2SAndroid Build Coastguard Worker     for (auto line_iterator = comment_lines.cbegin();
179*890232f2SAndroid Build Coastguard Worker          line_iterator != comment_lines.cend(); ++line_iterator) {
180*890232f2SAndroid Build Coastguard Worker       const auto &comment_line = *line_iterator;
181*890232f2SAndroid Build Coastguard Worker 
182*890232f2SAndroid Build Coastguard Worker       // remove leading and trailing spaces from comment line
183*890232f2SAndroid Build Coastguard Worker       const auto start = std::find_if(comment_line.begin(), comment_line.end(),
184*890232f2SAndroid Build Coastguard Worker                                       [](char c) { return !isspace(c); });
185*890232f2SAndroid Build Coastguard Worker       const auto end =
186*890232f2SAndroid Build Coastguard Worker           std::find_if(comment_line.rbegin(), comment_line.rend(), [](char c) {
187*890232f2SAndroid Build Coastguard Worker             return !isspace(c);
188*890232f2SAndroid Build Coastguard Worker           }).base();
189*890232f2SAndroid Build Coastguard Worker       if (start < end) {
190*890232f2SAndroid Build Coastguard Worker         comment.append(start, end);
191*890232f2SAndroid Build Coastguard Worker       } else {
192*890232f2SAndroid Build Coastguard Worker         comment.append(comment_line);
193*890232f2SAndroid Build Coastguard Worker       }
194*890232f2SAndroid Build Coastguard Worker 
195*890232f2SAndroid Build Coastguard Worker       if (line_iterator + 1 != comment_lines.cend()) comment.append("\n");
196*890232f2SAndroid Build Coastguard Worker     }
197*890232f2SAndroid Build Coastguard Worker     if (!comment.empty()) {
198*890232f2SAndroid Build Coastguard Worker       std::string description;
199*890232f2SAndroid Build Coastguard Worker       if (EscapeString(comment.c_str(), comment.length(), &description, true,
200*890232f2SAndroid Build Coastguard Worker                        true)) {
201*890232f2SAndroid Build Coastguard Worker         return description;
202*890232f2SAndroid Build Coastguard Worker       }
203*890232f2SAndroid Build Coastguard Worker       return "";
204*890232f2SAndroid Build Coastguard Worker     }
205*890232f2SAndroid Build Coastguard Worker     return "";
206*890232f2SAndroid Build Coastguard Worker   }
207*890232f2SAndroid Build Coastguard Worker 
generate()208*890232f2SAndroid Build Coastguard Worker   bool generate() {
209*890232f2SAndroid Build Coastguard Worker     code_ = "";
210*890232f2SAndroid Build Coastguard Worker     if (parser_.root_struct_def_ == nullptr) {
211*890232f2SAndroid Build Coastguard Worker       std::cerr << "Error: Binary schema not generated, no root struct found\n";
212*890232f2SAndroid Build Coastguard Worker       return false;
213*890232f2SAndroid Build Coastguard Worker     }
214*890232f2SAndroid Build Coastguard Worker     code_ += "{" + NewLine();
215*890232f2SAndroid Build Coastguard Worker     code_ += Indent(1) +
216*890232f2SAndroid Build Coastguard Worker              "\"$schema\": \"https://json-schema.org/draft/2019-09/schema\"," +
217*890232f2SAndroid Build Coastguard Worker              NewLine();
218*890232f2SAndroid Build Coastguard Worker     code_ += Indent(1) + "\"definitions\": {" + NewLine();
219*890232f2SAndroid Build Coastguard Worker     for (auto e = parser_.enums_.vec.cbegin(); e != parser_.enums_.vec.cend();
220*890232f2SAndroid Build Coastguard Worker          ++e) {
221*890232f2SAndroid Build Coastguard Worker       code_ += Indent(2) + "\"" + GenFullName(*e) + "\" : {" + NewLine();
222*890232f2SAndroid Build Coastguard Worker       code_ += Indent(3) + GenType("string") + "," + NewLine();
223*890232f2SAndroid Build Coastguard Worker       auto enumdef(Indent(3) + "\"enum\": [");
224*890232f2SAndroid Build Coastguard Worker       for (auto enum_value = (*e)->Vals().begin();
225*890232f2SAndroid Build Coastguard Worker            enum_value != (*e)->Vals().end(); ++enum_value) {
226*890232f2SAndroid Build Coastguard Worker         enumdef.append("\"" + (*enum_value)->name + "\"");
227*890232f2SAndroid Build Coastguard Worker         if (*enum_value != (*e)->Vals().back()) { enumdef.append(", "); }
228*890232f2SAndroid Build Coastguard Worker       }
229*890232f2SAndroid Build Coastguard Worker       enumdef.append("]");
230*890232f2SAndroid Build Coastguard Worker       code_ += enumdef + NewLine();
231*890232f2SAndroid Build Coastguard Worker       code_ += Indent(2) + "}," + NewLine();  // close type
232*890232f2SAndroid Build Coastguard Worker     }
233*890232f2SAndroid Build Coastguard Worker     for (auto s = parser_.structs_.vec.cbegin();
234*890232f2SAndroid Build Coastguard Worker          s != parser_.structs_.vec.cend(); ++s) {
235*890232f2SAndroid Build Coastguard Worker       const auto &structure = *s;
236*890232f2SAndroid Build Coastguard Worker       code_ += Indent(2) + "\"" + GenFullName(structure) + "\" : {" + NewLine();
237*890232f2SAndroid Build Coastguard Worker       code_ += Indent(3) + GenType("object") + "," + NewLine();
238*890232f2SAndroid Build Coastguard Worker       const auto &comment_lines = structure->doc_comment;
239*890232f2SAndroid Build Coastguard Worker       auto comment = PrepareDescription(comment_lines);
240*890232f2SAndroid Build Coastguard Worker       if (comment != "") {
241*890232f2SAndroid Build Coastguard Worker         code_ += Indent(3) + "\"description\" : " + comment + "," + NewLine();
242*890232f2SAndroid Build Coastguard Worker       }
243*890232f2SAndroid Build Coastguard Worker 
244*890232f2SAndroid Build Coastguard Worker       code_ += Indent(3) + "\"properties\" : {" + NewLine();
245*890232f2SAndroid Build Coastguard Worker 
246*890232f2SAndroid Build Coastguard Worker       const auto &properties = structure->fields.vec;
247*890232f2SAndroid Build Coastguard Worker       for (auto prop = properties.cbegin(); prop != properties.cend(); ++prop) {
248*890232f2SAndroid Build Coastguard Worker         const auto &property = *prop;
249*890232f2SAndroid Build Coastguard Worker         std::string arrayInfo = "";
250*890232f2SAndroid Build Coastguard Worker         if (IsArray(property->value.type)) {
251*890232f2SAndroid Build Coastguard Worker           arrayInfo = "," + NewLine() + Indent(8) + "\"minItems\": " +
252*890232f2SAndroid Build Coastguard Worker                       NumToString(property->value.type.fixed_length) + "," +
253*890232f2SAndroid Build Coastguard Worker                       NewLine() + Indent(8) + "\"maxItems\": " +
254*890232f2SAndroid Build Coastguard Worker                       NumToString(property->value.type.fixed_length);
255*890232f2SAndroid Build Coastguard Worker         }
256*890232f2SAndroid Build Coastguard Worker         std::string deprecated_info = "";
257*890232f2SAndroid Build Coastguard Worker         if (property->deprecated) {
258*890232f2SAndroid Build Coastguard Worker           deprecated_info =
259*890232f2SAndroid Build Coastguard Worker               "," + NewLine() + Indent(8) + "\"deprecated\" : true";
260*890232f2SAndroid Build Coastguard Worker         }
261*890232f2SAndroid Build Coastguard Worker         std::string typeLine = Indent(4) + "\"" + property->name + "\"";
262*890232f2SAndroid Build Coastguard Worker         typeLine += " : {" + NewLine() + Indent(8);
263*890232f2SAndroid Build Coastguard Worker         typeLine += GenType(property->value.type);
264*890232f2SAndroid Build Coastguard Worker         typeLine += arrayInfo;
265*890232f2SAndroid Build Coastguard Worker         typeLine += deprecated_info;
266*890232f2SAndroid Build Coastguard Worker         auto description = PrepareDescription(property->doc_comment);
267*890232f2SAndroid Build Coastguard Worker         if (description != "") {
268*890232f2SAndroid Build Coastguard Worker           typeLine +=
269*890232f2SAndroid Build Coastguard Worker               "," + NewLine() + Indent(8) + "\"description\" : " + description;
270*890232f2SAndroid Build Coastguard Worker         }
271*890232f2SAndroid Build Coastguard Worker 
272*890232f2SAndroid Build Coastguard Worker         typeLine += NewLine() + Indent(7) + "}";
273*890232f2SAndroid Build Coastguard Worker         if (property != properties.back()) { typeLine.append(","); }
274*890232f2SAndroid Build Coastguard Worker         code_ += typeLine + NewLine();
275*890232f2SAndroid Build Coastguard Worker       }
276*890232f2SAndroid Build Coastguard Worker       code_ += Indent(3) + "}," + NewLine();  // close properties
277*890232f2SAndroid Build Coastguard Worker 
278*890232f2SAndroid Build Coastguard Worker       std::vector<FieldDef *> requiredProperties;
279*890232f2SAndroid Build Coastguard Worker       std::copy_if(properties.begin(), properties.end(),
280*890232f2SAndroid Build Coastguard Worker                    back_inserter(requiredProperties),
281*890232f2SAndroid Build Coastguard Worker                    [](FieldDef const *prop) { return prop->IsRequired(); });
282*890232f2SAndroid Build Coastguard Worker       if (!requiredProperties.empty()) {
283*890232f2SAndroid Build Coastguard Worker         auto required_string(Indent(3) + "\"required\" : [");
284*890232f2SAndroid Build Coastguard Worker         for (auto req_prop = requiredProperties.cbegin();
285*890232f2SAndroid Build Coastguard Worker              req_prop != requiredProperties.cend(); ++req_prop) {
286*890232f2SAndroid Build Coastguard Worker           required_string.append("\"" + (*req_prop)->name + "\"");
287*890232f2SAndroid Build Coastguard Worker           if (*req_prop != requiredProperties.back()) {
288*890232f2SAndroid Build Coastguard Worker             required_string.append(", ");
289*890232f2SAndroid Build Coastguard Worker           }
290*890232f2SAndroid Build Coastguard Worker         }
291*890232f2SAndroid Build Coastguard Worker         required_string.append("],");
292*890232f2SAndroid Build Coastguard Worker         code_ += required_string + NewLine();
293*890232f2SAndroid Build Coastguard Worker       }
294*890232f2SAndroid Build Coastguard Worker       code_ += Indent(3) + "\"additionalProperties\" : false" + NewLine();
295*890232f2SAndroid Build Coastguard Worker       auto closeType(Indent(2) + "}");
296*890232f2SAndroid Build Coastguard Worker       if (*s != parser_.structs_.vec.back()) { closeType.append(","); }
297*890232f2SAndroid Build Coastguard Worker       code_ += closeType + NewLine();  // close type
298*890232f2SAndroid Build Coastguard Worker     }
299*890232f2SAndroid Build Coastguard Worker     code_ += Indent(1) + "}," + NewLine();  // close definitions
300*890232f2SAndroid Build Coastguard Worker 
301*890232f2SAndroid Build Coastguard Worker     // mark root type
302*890232f2SAndroid Build Coastguard Worker     code_ += Indent(1) + "\"$ref\" : \"#/definitions/" +
303*890232f2SAndroid Build Coastguard Worker              GenFullName(parser_.root_struct_def_) + "\"" + NewLine();
304*890232f2SAndroid Build Coastguard Worker 
305*890232f2SAndroid Build Coastguard Worker     code_ += "}" + NewLine();  // close schema root
306*890232f2SAndroid Build Coastguard Worker     return true;
307*890232f2SAndroid Build Coastguard Worker   }
308*890232f2SAndroid Build Coastguard Worker 
save() const309*890232f2SAndroid Build Coastguard Worker   bool save() const {
310*890232f2SAndroid Build Coastguard Worker     const auto file_path = GeneratedFileName(path_, file_name_, parser_.opts);
311*890232f2SAndroid Build Coastguard Worker     return SaveFile(file_path.c_str(), code_, false);
312*890232f2SAndroid Build Coastguard Worker   }
313*890232f2SAndroid Build Coastguard Worker 
getJson()314*890232f2SAndroid Build Coastguard Worker   const std::string getJson() { return code_; }
315*890232f2SAndroid Build Coastguard Worker };
316*890232f2SAndroid Build Coastguard Worker }  // namespace jsons
317*890232f2SAndroid Build Coastguard Worker 
GenerateJsonSchema(const Parser & parser,const std::string & path,const std::string & file_name)318*890232f2SAndroid Build Coastguard Worker bool GenerateJsonSchema(const Parser &parser, const std::string &path,
319*890232f2SAndroid Build Coastguard Worker                         const std::string &file_name) {
320*890232f2SAndroid Build Coastguard Worker   jsons::JsonSchemaGenerator generator(parser, path, file_name);
321*890232f2SAndroid Build Coastguard Worker   if (!generator.generate()) { return false; }
322*890232f2SAndroid Build Coastguard Worker   return generator.save();
323*890232f2SAndroid Build Coastguard Worker }
324*890232f2SAndroid Build Coastguard Worker 
GenerateJsonSchema(const Parser & parser,std::string * json)325*890232f2SAndroid Build Coastguard Worker bool GenerateJsonSchema(const Parser &parser, std::string *json) {
326*890232f2SAndroid Build Coastguard Worker   jsons::JsonSchemaGenerator generator(parser, "", "");
327*890232f2SAndroid Build Coastguard Worker   if (!generator.generate()) { return false; }
328*890232f2SAndroid Build Coastguard Worker   *json = generator.getJson();
329*890232f2SAndroid Build Coastguard Worker   return true;
330*890232f2SAndroid Build Coastguard Worker }
331*890232f2SAndroid Build Coastguard Worker }  // namespace flatbuffers
332