Added a "strict JSON" mode to the text generator and compiler

This will add quotes around field names, as required by the official
standard. By default it will leave quotes out, as it is more readable,
more compact, and is accepted by almost all JSON parsers.
The -S switch to flatc turns on strict mode.

As per rfc 7159.

Change-Id: Ibabe9c8162c47339d00ec581d18721a2ba40c6d0
Tested: on Windows.
This commit is contained in:
Wouter van Oortmerssen
2014-07-08 16:35:14 -07:00
parent 9140144d51
commit 7fcbe723fc
11 changed files with 94 additions and 50 deletions

View File

@@ -25,7 +25,8 @@ namespace flatbuffers {
bool GenerateBinary(const Parser &parser,
const std::string &path,
const std::string &file_name) {
const std::string &file_name,
const GeneratorOptions & /*opts*/) {
return !parser.builder_.GetSize() ||
flatbuffers::SaveFile(
(path + file_name + "_wire.bin").c_str(),
@@ -36,11 +37,13 @@ bool GenerateBinary(const Parser &parser,
bool GenerateTextFile(const Parser &parser,
const std::string &path,
const std::string &file_name) {
const std::string &file_name,
const GeneratorOptions &opts) {
if (!parser.builder_.GetSize()) return true;
if (!parser.root_struct_def) Error("root_type not set");
std::string text;
GenerateText(parser, parser.builder_.GetBufferPointer(), 2, &text);
GenerateText(parser, parser.builder_.GetBufferPointer(), opts,
&text);
return flatbuffers::SaveFile((path + file_name + "_wire.txt").c_str(),
text,
false);
@@ -54,7 +57,8 @@ bool GenerateTextFile(const Parser &parser,
struct Generator {
bool (*generate)(const flatbuffers::Parser &parser,
const std::string &path,
const std::string &file_name);
const std::string &file_name,
const flatbuffers::GeneratorOptions &opts);
const char *extension;
const char *name;
const char *help;
@@ -82,6 +86,7 @@ static void Error(const char *err, const char *obj, bool usage) {
for (size_t i = 0; i < sizeof(generators) / sizeof(generators[0]); ++i)
printf(" -%s %s.\n", generators[i].extension, generators[i].help);
printf(" -o PATH Prefix PATH to all generated files.\n"
" -S Strict JSON: add quotes to field names.\n"
"FILEs may depend on declarations in earlier files.\n"
"Output files are named using the base file name of the input,"
"and written to the current directory or the path given by -o.\n"
@@ -110,6 +115,7 @@ std::string StripPath(const std::string &filename) {
int main(int argc, const char *argv[]) {
program_name = argv[0];
flatbuffers::Parser parser;
flatbuffers::GeneratorOptions opts;
std::string output_path;
const size_t num_generators = sizeof(generators) / sizeof(generators[0]);
bool generator_enabled[num_generators] = { false };
@@ -127,6 +133,9 @@ int main(int argc, const char *argv[]) {
if (++i >= argc) Error("missing path following", arg, true);
output_path = argv[i];
break;
case 'S':
opts.strict_json = true;
break;
default:
for (size_t i = 0; i < num_generators; ++i) {
if(!strcmp(arg+1, generators[i].extension)) {
@@ -165,7 +174,7 @@ int main(int argc, const char *argv[]) {
for (size_t i = 0; i < num_generators; ++i) {
if (generator_enabled[i]) {
if (!generators[i].generate(parser, output_path, filebase)) {
if (!generators[i].generate(parser, output_path, filebase, opts)) {
Error((std::string("Unable to generate ") +
generators[i].name +
" for " +

View File

@@ -486,7 +486,8 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
bool GenerateCPP(const Parser &parser,
const std::string &path,
const std::string &file_name) {
const std::string &file_name,
const GeneratorOptions & /*opts*/) {
auto code = GenerateCPP(parser, file_name);
return !code.length() ||
SaveFile((path + file_name + "_generated.h").c_str(), code, false);

View File

@@ -380,7 +380,8 @@ static bool SaveClass(const Parser &parser, const Definition &def,
bool GenerateJava(const Parser &parser,
const std::string &path,
const std::string & /*file_name*/) {
const std::string & /*file_name*/,
const GeneratorOptions & /*opts*/) {
using namespace java;
for (auto it = parser.enums_.vec.begin();

View File

@@ -23,7 +23,8 @@
namespace flatbuffers {
static void GenStruct(const StructDef &struct_def, const Table *table,
int indent, int indent_step, std::string *_text);
int indent, const GeneratorOptions &opts,
std::string *_text);
// If indentation is less than 0, that indicates we don't want any newlines
// either.
@@ -35,7 +36,8 @@ const char *NewLine(int indent_step) {
// for a single FlatBuffer value into JSON format.
// The general case for scalars:
template<typename T> void Print(T val, Type /*type*/, int /*indent*/,
int /*indent_step*/, StructDef * /*union_sd*/,
StructDef * /*union_sd*/,
const GeneratorOptions & /*opts*/,
std::string *_text) {
std::string &text = *_text;
text += NumToString(val);
@@ -43,24 +45,25 @@ template<typename T> void Print(T val, Type /*type*/, int /*indent*/,
// Print a vector a sequence of JSON values, comma separated, wrapped in "[]".
template<typename T> void PrintVector(const Vector<T> &v, Type type,
int indent, int indent_step,
int indent, const GeneratorOptions &opts,
std::string *_text) {
std::string &text = *_text;
text += "[";
text += NewLine(indent_step);
text += NewLine(opts.indent_step);
for (uoffset_t i = 0; i < v.Length(); i++) {
if (i) {
text += ",";
text += NewLine(indent_step);
text += NewLine(opts.indent_step);
}
text.append(indent + indent_step, ' ');
text.append(indent + opts.indent_step, ' ');
if (IsStruct(type))
Print(v.GetStructFromOffset(i * type.struct_def->bytesize), type,
indent + indent_step, indent_step, nullptr, _text);
indent + opts.indent_step, nullptr, opts, _text);
else
Print(v.Get(i), type, indent + indent_step, indent_step, nullptr, _text);
Print(v.Get(i), type, indent + opts.indent_step, nullptr,
opts, _text);
}
text += NewLine(indent_step);
text += NewLine(opts.indent_step);
text.append(indent, ' ');
text += "]";
}
@@ -91,8 +94,10 @@ static void EscapeString(const String &s, std::string *_text) {
// Specialization of Print above for pointer types.
template<> void Print<const void *>(const void *val,
Type type, int indent, int indent_step,
StructDef *union_sd, std::string *_text) {
Type type, int indent,
StructDef *union_sd,
const GeneratorOptions &opts,
std::string *_text) {
switch (type.base_type) {
case BASE_TYPE_UNION:
// If this assert hits, you have an corrupt buffer, a union type field
@@ -101,14 +106,14 @@ template<> void Print<const void *>(const void *val,
GenStruct(*union_sd,
reinterpret_cast<const Table *>(val),
indent,
indent_step,
opts,
_text);
break;
case BASE_TYPE_STRUCT:
GenStruct(*type.struct_def,
reinterpret_cast<const Table *>(val),
indent,
indent_step,
opts,
_text);
break;
case BASE_TYPE_STRING: {
@@ -123,7 +128,7 @@ template<> void Print<const void *>(const void *val,
case BASE_TYPE_ ## ENUM: \
PrintVector<CTYPE>( \
*reinterpret_cast<const Vector<CTYPE> *>(val), \
type, indent, indent_step, _text); break;
type, indent, opts, _text); break;
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
}
@@ -135,18 +140,19 @@ template<> void Print<const void *>(const void *val,
// Generate text for a scalar field.
template<typename T> static void GenField(const FieldDef &fd,
const Table *table, bool fixed,
int indent_step, int indent,
const GeneratorOptions &opts,
int indent,
std::string *_text) {
Print(fixed ?
reinterpret_cast<const Struct *>(table)->GetField<T>(fd.value.offset) :
table->GetField<T>(fd.value.offset, 0), fd.value.type, indent, indent_step,
nullptr, _text);
table->GetField<T>(fd.value.offset, 0), fd.value.type, indent, nullptr,
opts, _text);
}
// Generate text for non-scalar field.
static void GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed,
int indent, int indent_step, StructDef *union_sd,
std::string *_text) {
int indent, StructDef *union_sd,
const GeneratorOptions &opts, std::string *_text) {
const void *val = nullptr;
if (fixed) {
// The only non-scalar fields in structs are structs.
@@ -158,16 +164,17 @@ static void GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed,
? table->GetStruct<const void *>(fd.value.offset)
: table->GetPointer<const void *>(fd.value.offset);
}
Print(val, fd.value.type, indent, indent_step, union_sd, _text);
Print(val, fd.value.type, indent, union_sd, opts, _text);
}
// Generate text for a struct or table, values separated by commas, indented,
// and bracketed by "{}"
static void GenStruct(const StructDef &struct_def, const Table *table,
int indent, int indent_step, std::string *_text) {
int indent, const GeneratorOptions &opts,
std::string *_text) {
std::string &text = *_text;
text += "{";
text += NewLine(indent_step);
text += NewLine(opts.indent_step);
int fieldout = 0;
StructDef *union_sd = nullptr;
for (auto it = struct_def.fields.vec.begin();
@@ -178,16 +185,18 @@ static void GenStruct(const StructDef &struct_def, const Table *table,
// The field is present.
if (fieldout++) {
text += ",";
text += NewLine(indent_step);
text += NewLine(opts.indent_step);
}
text.append(indent + indent_step, ' ');
text.append(indent + opts.indent_step, ' ');
if (opts.strict_json) text += "\"";
text += fd.name;
if (opts.strict_json) text += "\"";
text += ": ";
switch (fd.value.type.base_type) {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) \
case BASE_TYPE_ ## ENUM: \
GenField<CTYPE>(fd, table, struct_def.fixed, \
indent + indent_step, indent_step, _text); \
opts, indent + opts.indent_step, _text); \
break;
FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
@@ -196,8 +205,8 @@ static void GenStruct(const StructDef &struct_def, const Table *table,
case BASE_TYPE_ ## ENUM:
FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
GenFieldOffset(fd, table, struct_def.fixed, indent + indent_step,
indent_step, union_sd, _text);
GenFieldOffset(fd, table, struct_def.fixed, indent + opts.indent_step,
union_sd, opts, _text);
break;
}
if (fd.value.type.base_type == BASE_TYPE_UTYPE) {
@@ -206,23 +215,23 @@ static void GenStruct(const StructDef &struct_def, const Table *table,
}
}
}
text += NewLine(indent_step);
text += NewLine(opts.indent_step);
text.append(indent, ' ');
text += "}";
}
// Generate a text representation of a flatbuffer in JSON format.
void GenerateText(const Parser &parser, const void *flatbuffer,
int indent_step, std::string *_text) {
const GeneratorOptions &opts, std::string *_text) {
std::string &text = *_text;
assert(parser.root_struct_def); // call SetRootType()
text.reserve(1024); // Reduce amount of inevitable reallocs.
GenStruct(*parser.root_struct_def,
GetRoot<Table>(flatbuffer),
0,
indent_step,
opts,
_text);
text += NewLine(indent_step);
text += NewLine(opts.indent_step);
}
} // namespace flatbuffers

View File

@@ -669,7 +669,7 @@ void Parser::ParseDecl() {
// Check if this is a table that has manual id assignments
auto &fields = struct_def.fields.vec;
if (!struct_def.fixed && fields.size()) {
int num_id_fields = 0;
size_t num_id_fields = 0;
for (auto it = fields.begin(); it != fields.end(); ++it) {
if ((*it)->attributes.Lookup("id")) num_id_fields++;
}