Add generation of JSON Schema to library (#6165)

* Fix C/C++ Create<Type>Direct with sorted vectors

If a struct has a key the vector has to be sorted. To sort the vector
you can't use "const".

* Changes due to code review

* Improve code readability

* Add generate of JSON schema to string to lib

* option indent_step is supported

* Remove unused variables

* Fix break in test

* Fix style to be consistent with rest of the code
This commit is contained in:
tira-misu
2020-10-16 00:28:25 +02:00
committed by GitHub
parent 5be777e1d4
commit 5cd7137103
2 changed files with 68 additions and 35 deletions

View File

@@ -1008,6 +1008,10 @@ extern bool GenerateText(const Parser &parser, const void *flatbuffer,
extern bool GenerateTextFile(const Parser &parser, const std::string &path, extern bool GenerateTextFile(const Parser &parser, const std::string &path,
const std::string &file_name); const std::string &file_name);
// Generate Json schema to string
// See idl_gen_json_schema.cpp.
extern bool GenerateJsonSchema(const Parser &parser, std::string *json);
// Generate binary files from a given FlatBuffer, and a given Parser // Generate binary files from a given FlatBuffer, and a given Parser
// object that has been populated with the corresponding schema. // object that has been populated with the corresponding schema.
// See code_generators.cpp. // See code_generators.cpp.

View File

@@ -106,7 +106,7 @@ std::string GenType(const Type &type) {
class JsonSchemaGenerator : public BaseGenerator { class JsonSchemaGenerator : public BaseGenerator {
private: private:
CodeWriter code_; std::string code_;
public: public:
JsonSchemaGenerator(const Parser &parser, const std::string &path, JsonSchemaGenerator(const Parser &parser, const std::string &path,
@@ -123,31 +123,44 @@ class JsonSchemaGenerator : public BaseGenerator {
return path + file_name + ".schema.json"; return path + file_name + ".schema.json";
} }
// If indentation is less than 0, that indicates we don't want any newlines
// either.
const std::string NewLine() {
return parser_.opts.indent_step >= 0 ? "\n" : "";
}
const std::string Indent(int indent) {
std::string indentation = "";
return indentation.append(indent * std::max(parser_.opts.indent_step, 0), ' ');
}
bool generate() { bool generate() {
code_ = "";
if (parser_.root_struct_def_ == nullptr) { return false; } if (parser_.root_struct_def_ == nullptr) { return false; }
code_.Clear(); code_ += "{" + NewLine();
code_ += "{"; code_ += Indent(1) +
code_ += " \"$schema\": \"https://json-schema.org/draft/2019-09/schema\","; "\"$schema\": \"https://json-schema.org/draft/2019-09/schema\"," +
code_ += " \"definitions\": {"; NewLine();
code_ += Indent(1) + "\"definitions\": {" + NewLine();
for (auto e = parser_.enums_.vec.cbegin(); e != parser_.enums_.vec.cend(); for (auto e = parser_.enums_.vec.cbegin(); e != parser_.enums_.vec.cend();
++e) { ++e) {
code_ += " \"" + GenFullName(*e) + "\" : {"; code_ += Indent(2) + "\"" + GenFullName(*e) + "\" : {" + NewLine();
code_ += " " + GenType("string") + ","; code_ += Indent(3) + GenType("string") + "," + NewLine();
std::string enumdef(" \"enum\": ["); std::string enumdef(Indent(3) + "\"enum\": [");
for (auto enum_value = (*e)->Vals().begin(); for (auto enum_value = (*e)->Vals().begin();
enum_value != (*e)->Vals().end(); ++enum_value) { enum_value != (*e)->Vals().end(); ++enum_value) {
enumdef.append("\"" + (*enum_value)->name + "\""); enumdef.append("\"" + (*enum_value)->name + "\"");
if (*enum_value != (*e)->Vals().back()) { enumdef.append(", "); } if (*enum_value != (*e)->Vals().back()) { enumdef.append(", "); }
} }
enumdef.append("]"); enumdef.append("]");
code_ += enumdef; code_ += enumdef + NewLine();
code_ += " },"; // close type code_ += Indent(2) + "}," + NewLine(); // close type
} }
for (auto s = parser_.structs_.vec.cbegin(); for (auto s = parser_.structs_.vec.cbegin();
s != parser_.structs_.vec.cend(); ++s) { s != parser_.structs_.vec.cend(); ++s) {
const auto &structure = *s; const auto &structure = *s;
code_ += " \"" + GenFullName(structure) + "\" : {"; code_ += Indent(2) + "\"" + GenFullName(structure) + "\" : {" + NewLine();
code_ += " " + GenType("object") + ","; code_ += Indent(3) + GenType("object") + "," + NewLine();
std::string comment; std::string comment;
const auto &comment_lines = structure->doc_comment; const auto &comment_lines = structure->doc_comment;
for (auto comment_line = comment_lines.cbegin(); for (auto comment_line = comment_lines.cbegin();
@@ -160,41 +173,42 @@ class JsonSchemaGenerator : public BaseGenerator {
true)) { true)) {
return false; return false;
} }
code_ += " \"description\" : " + description + ","; code_ +=
Indent(3) + "\"description\" : " + description + "," + NewLine();
} }
code_ += " \"properties\" : {"; code_ += Indent(3) + "\"properties\" : {" + NewLine();
const auto &properties = structure->fields.vec; const auto &properties = structure->fields.vec;
for (auto prop = properties.cbegin(); prop != properties.cend(); ++prop) { for (auto prop = properties.cbegin(); prop != properties.cend(); ++prop) {
const auto &property = *prop; const auto &property = *prop;
std::string arrayInfo = ""; std::string arrayInfo = "";
if (IsArray(property->value.type)) { if (IsArray(property->value.type)) {
arrayInfo = ",\n \"minItems\": " + arrayInfo = "," + NewLine() + Indent(8) + "\"minItems\": " +
NumToString(property->value.type.fixed_length) + NumToString(property->value.type.fixed_length) +
",\n \"maxItems\": " + "," + NewLine() + Indent(8) + "\"maxItems\": " +
NumToString(property->value.type.fixed_length); NumToString(property->value.type.fixed_length);
} }
std::string deprecated_info = ""; std::string deprecated_info = "";
if (property->deprecated) { if (property->deprecated) {
deprecated_info = ",\n \"deprecated\" : true,"; deprecated_info = "," + NewLine() + Indent(8) + "\"deprecated\" : true,";
} }
std::string typeLine = std::string typeLine = Indent(4) + "\"" + property->name + "\"";
" \"" + property->name + "\" : {\n" + " "; typeLine += " : {" + NewLine() + Indent(8);
typeLine += GenType(property->value.type); typeLine += GenType(property->value.type);
typeLine += arrayInfo; typeLine += arrayInfo;
typeLine += deprecated_info; typeLine += deprecated_info;
typeLine += "\n }"; typeLine += NewLine() + Indent(7) + "}";
if (property != properties.back()) { typeLine.append(","); } if (property != properties.back()) { typeLine.append(","); }
code_ += typeLine; code_ += typeLine + NewLine();
} }
code_ += " },"; // close properties code_ += Indent(3) + "}," + NewLine(); // close properties
std::vector<FieldDef *> requiredProperties; std::vector<FieldDef *> requiredProperties;
std::copy_if(properties.begin(), properties.end(), std::copy_if(properties.begin(), properties.end(),
back_inserter(requiredProperties), back_inserter(requiredProperties),
[](FieldDef const *prop) { return prop->required; }); [](FieldDef const *prop) { return prop->required; });
if (requiredProperties.size() > 0) { if (requiredProperties.size() > 0) {
std::string required_string(" \"required\" : ["); std::string required_string(Indent(3) + "\"required\" : [");
for (auto req_prop = requiredProperties.cbegin(); for (auto req_prop = requiredProperties.cbegin();
req_prop != requiredProperties.cend(); ++req_prop) { req_prop != requiredProperties.cend(); ++req_prop) {
required_string.append("\"" + (*req_prop)->name + "\""); required_string.append("\"" + (*req_prop)->name + "\"");
@@ -203,24 +217,31 @@ class JsonSchemaGenerator : public BaseGenerator {
} }
} }
required_string.append("],"); required_string.append("],");
code_ += required_string; code_ += required_string + NewLine();
} }
code_ += " \"additionalProperties\" : false"; code_ += Indent(3) + "\"additionalProperties\" : false" + NewLine();
std::string closeType(" }"); std::string closeType(Indent(2) + "}");
if (*s != parser_.structs_.vec.back()) { closeType.append(","); } if (*s != parser_.structs_.vec.back()) { closeType.append(","); }
code_ += closeType; // close type code_ += closeType + NewLine(); // close type
} }
code_ += " },"; // close definitions code_ += Indent(1) + "}," + NewLine(); // close definitions
// mark root type // mark root type
code_ += " \"$ref\" : \"#/definitions/" + code_ += Indent(1) + "\"$ref\" : \"#/definitions/" +
GenFullName(parser_.root_struct_def_) + "\""; GenFullName(parser_.root_struct_def_) + "\"" + NewLine();
code_ += "}"; // close schema root code_ += "}" + NewLine(); // close schema root
return true;
}
bool save() {
const std::string file_path = const std::string file_path =
GeneratedFileName(path_, file_name_, parser_.opts); GeneratedFileName(path_, file_name_, parser_.opts);
const std::string final_code = code_.ToString(); return SaveFile(file_path.c_str(), code_, false);
return SaveFile(file_path.c_str(), final_code, false); }
const std::string getJson() {
return code_;
} }
}; };
} // namespace jsons } // namespace jsons
@@ -228,6 +249,14 @@ class JsonSchemaGenerator : public BaseGenerator {
bool GenerateJsonSchema(const Parser &parser, const std::string &path, bool GenerateJsonSchema(const Parser &parser, const std::string &path,
const std::string &file_name) { const std::string &file_name) {
jsons::JsonSchemaGenerator generator(parser, path, file_name); jsons::JsonSchemaGenerator generator(parser, path, file_name);
return generator.generate(); if (!generator.generate()) { return false; }
return generator.save();
}
bool GenerateJsonSchema(const Parser &parser, std::string *json) {
jsons::JsonSchemaGenerator generator(parser, "", "");
if (!generator.generate()) { return false; }
*json = generator.getJson();
return true;
} }
} // namespace flatbuffers } // namespace flatbuffers