Skip to content

Commit

Permalink
Merge pull request #2776 from ncpenke/skip-unknown-json-fields
Browse files Browse the repository at this point in the history
Fix #2775: Add parser option to skip unknown JSON fields
  • Loading branch information
Wouter van Oortmerssen committed Dec 29, 2015
2 parents e6240f4 + f243109 commit 8f13c82
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 16 deletions.
7 changes: 6 additions & 1 deletion include/flatbuffers/idl.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ struct IDLOptions {
bool one_file;
bool proto_mode;
bool generate_all;
bool skip_unexpected_fields_in_json;

// Possible options for the more general generator below.
enum Language { kJava, kCSharp, kGo, kMAX };
Expand All @@ -337,6 +338,7 @@ struct IDLOptions {
one_file(false),
proto_mode(false),
generate_all(false),
skip_unexpected_fields_in_json(false),
lang(IDLOptions::kJava) {}
};

Expand Down Expand Up @@ -477,7 +479,10 @@ class Parser {
CHECKED_ERROR ParseProtoDecl();
CHECKED_ERROR ParseProtoCurliesOrIdent();
CHECKED_ERROR ParseTypeFromProtoType(Type *type);

CHECKED_ERROR SkipAnyJsonValue();
CHECKED_ERROR SkipJsonObject();
CHECKED_ERROR SkipJsonArray();
CHECKED_ERROR SkipJsonString();
CHECKED_ERROR DoParse(const char *_source, const char **include_paths,
const char *source_filename);

Expand Down
7 changes: 6 additions & 1 deletion src/flatc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,15 @@ static void Error(const std::string &err, bool usage, bool show_exe_name) {
" no trailing commas in tables/vectors.\n"
" --defaults-json Output fields whose value is the default when\n"
" writing JSON\n"
" --unknown-json Allow fields in JSON that are not defined in the\n"
" schema. These fields will be discared when generating\n"
" binaries.\n"
" --no-prefix Don\'t prefix enum values with the enum type in C++.\n"
" --scoped-enums Use C++11 style scoped and strongly typed enums.\n"
" also implies --no-prefix.\n"
" --gen-includes (deprecated), this is the default behavior.\n"
" If the original behavior is required (no include\n"
" statements) use --no-includes.\n"
" statements) use --no-includes.\n"
" --no-includes Don\'t generate include statements for included\n"
" schemas the generated file depends on (C++).\n"
" --gen-mutable Generate accessors that can mutate buffers in-place.\n"
Expand Down Expand Up @@ -155,6 +158,8 @@ int main(int argc, const char *argv[]) {
opts.skip_js_exports = true;
} else if(arg == "--defaults-json") {
opts.output_default_scalars_in_json = true;
} else if (arg == "--unknown-json") {
opts.skip_unexpected_fields_in_json = true;
} else if(arg == "--no-prefix") {
opts.prefixed_enums = false;
} else if(arg == "--scoped-enums") {
Expand Down
103 changes: 89 additions & 14 deletions src/idl_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -673,24 +673,33 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
EXPECT(opts.strict_json ? kTokenStringConstant : kTokenIdentifier);
}
auto field = struct_def.fields.Lookup(name);
if (!field) return Error("unknown field: " + name);
EXPECT(':');
Value val = field->value;
ECHECK(ParseAnyValue(val, field, fieldn));
size_t i = field_stack_.size();
// Hardcoded insertion-sort with error-check.
// If fields are specified in order, then this loop exits immediately.
for (; i > field_stack_.size() - fieldn; i--) {
auto existing_field = field_stack_[i - 1].second;
if (existing_field == field)
return Error("field set more than once: " + field->name);
if (existing_field->value.offset < field->value.offset) break;
if (!field) {
if (!opts.skip_unexpected_fields_in_json) {
return Error("unknown field: " + name);
} else {
EXPECT(':');
ECHECK(SkipAnyJsonValue());
}
} else {
EXPECT(':');
Value val = field->value;
ECHECK(ParseAnyValue(val, field, fieldn));
size_t i = field_stack_.size();
// Hardcoded insertion-sort with error-check.
// If fields are specified in order, then this loop exits immediately.
for (; i > field_stack_.size() - fieldn; i--) {
auto existing_field = field_stack_[i - 1].second;
if (existing_field == field)
return Error("field set more than once: " + field->name);
if (existing_field->value.offset < field->value.offset) break;
}
field_stack_.insert(field_stack_.begin() + i, std::make_pair(val, field));
fieldn++;
}
field_stack_.insert(field_stack_.begin() + i, std::make_pair(val, field));
fieldn++;
if (Is('}')) { NEXT(); break; }
EXPECT(',');
}

if (struct_def.fixed && fieldn != struct_def.fields.vec.size())
return Error("struct: wrong number of initializers: " + struct_def.name);

Expand Down Expand Up @@ -1480,6 +1489,72 @@ CheckedError Parser::ParseTypeFromProtoType(Type *type) {
return NoError();
}

CheckedError Parser::SkipAnyJsonValue() {
switch (token_) {
case '{':
ECHECK(SkipJsonObject());
break;
case kTokenStringConstant:
ECHECK(SkipJsonString());
break;
case '[':
ECHECK(SkipJsonArray());
break;
case kTokenIntegerConstant:
EXPECT(kTokenIntegerConstant);
break;
case kTokenFloatConstant:
EXPECT(kTokenFloatConstant);
break;
default:
return Error(std::string("Unexpected token:") + std::string(1, token_));
}
return NoError();
}

CheckedError Parser::SkipJsonObject() {
EXPECT('{');
size_t fieldn = 0;

while (true) {
if ((!opts.strict_json || !fieldn) && Is('}')) break;

if (!Is(kTokenStringConstant))
EXPECT(opts.strict_json ? kTokenStringConstant : kTokenIdentifier);

EXPECT(':');
ECHECK(SkipAnyJsonValue());
fieldn++;

if (Is('}')) break;
EXPECT(',');
}

NEXT();
return NoError();
}

CheckedError Parser::SkipJsonArray() {
EXPECT('[');

while (true) {
if (Is(']')) break;

ECHECK(SkipAnyJsonValue());

if (Is(']')) break;
EXPECT(',');
}

NEXT();
return NoError();
}

CheckedError Parser::SkipJsonString() {
EXPECT(kTokenStringConstant);
return NoError();
}

bool Parser::Parse(const char *source, const char **include_paths,
const char *source_filename) {
return !DoParse(source, include_paths, source_filename).Check();
Expand Down
21 changes: 21 additions & 0 deletions tests/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,26 @@ void UnicodeTest() {
"\\u5225\\u30B5\\u30A4\\u30C8\\x01\\x80\"}", true);
}

void UnknownFieldsTest() {
flatbuffers::IDLOptions opts;
opts.skip_unexpected_fields_in_json = true;
flatbuffers::Parser parser(opts);

TEST_EQ(parser.Parse("table T { str:string; i:int;}"
"root_type T;"
"{ str:\"test\","
"unknown_int:10,"
"unknown_float:1.0,"
"unknown_array: [ 1, 2, 3, 4],"
"unknown_object: { i: 10 },"
"i:10}"), true);

std::string jsongen;
parser.opts.indent_step = -1;
GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
TEST_EQ(jsongen == "{str: \"test\",i: 10}", true);
}

int main(int /*argc*/, const char * /*argv*/[]) {
// Run our various test suites:

Expand All @@ -837,6 +857,7 @@ int main(int /*argc*/, const char * /*argv*/[]) {
ScientificTest();
EnumStringsTest();
UnicodeTest();
UnknownFieldsTest();

if (!testing_fails) {
TEST_OUTPUT_LINE("ALL TESTS PASSED");
Expand Down

0 comments on commit 8f13c82

Please sign in to comment.