diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 0f500243d..386c38d4a 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -232,7 +232,7 @@ struct Definition { struct FieldDef : public Definition { FieldDef() : deprecated(false), required(false), key(false), - flexbuffer(false), padding(0) {} + flexbuffer(false), nested_flatbuffer(NULL), padding(0) {} Offset Serialize(FlatBufferBuilder *builder, uint16_t id, const Parser &parser) const; @@ -245,6 +245,7 @@ struct FieldDef : public Definition { bool native_inline; // Field will be defined inline (instead of as a pointer) // for native tables if field is a struct. bool flexbuffer; // This field contains FlexBuffer data. + StructDef *nested_flatbuffer; // This field contains nested FlatBuffer data. size_t padding; // Bytes to always pad after this field. }; @@ -602,6 +603,9 @@ private: FLATBUFFERS_CHECKED_ERROR ParseVectorDelimiters( size_t &count, ParseVectorDelimitersBody body, void *state); FLATBUFFERS_CHECKED_ERROR ParseVector(const Type &type, uoffset_t *ovalue); + FLATBUFFERS_CHECKED_ERROR ParseNestedFlatbuffer(Value &val, FieldDef *field, + size_t parent_fieldn, + const StructDef *parent_struct_def); FLATBUFFERS_CHECKED_ERROR ParseMetaData(SymbolTable *attributes); FLATBUFFERS_CHECKED_ERROR TryTypedValue(int dtoken, bool check, Value &e, BaseType req, bool *destmatch); diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index fb5a822ab..5c8586d6b 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -1065,28 +1065,24 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { } } // generate object accessors if is nested_flatbuffer - auto nested = field.attributes.Lookup("nested_flatbuffer"); - if (nested) { - auto nested_qualified_name = - parser_.namespaces_.back()->GetFullyQualifiedName(nested->constant); - auto nested_type = parser_.structs_.Lookup(nested_qualified_name); - auto nested_type_name = WrapInNameSpace(*nested_type); - auto nestedMethodName = MakeCamel(field.name, lang_.first_camel_upper) + if (field.nested_flatbuffer) { + auto nested_type_name = WrapInNameSpace(*field.nested_flatbuffer); + auto nested_method_name = MakeCamel(field.name, lang_.first_camel_upper) + "As" + nested_type_name; - auto getNestedMethodName = nestedMethodName; + auto get_nested_method_name = nested_method_name; if (lang_.language == IDLOptions::kCSharp) { - getNestedMethodName = "Get" + nestedMethodName; + get_nested_method_name = "Get" + nested_method_name; conditional_cast = "(" + nested_type_name + lang_.optional_suffix + ")"; } if (lang_.language != IDLOptions::kCSharp) { code += " public " + nested_type_name + lang_.optional_suffix + " "; - code += nestedMethodName + "() { return "; - code += getNestedMethodName + "(new " + nested_type_name + "()); }\n"; + code += nested_method_name + "() { return "; + code += get_nested_method_name + "(new " + nested_type_name + "()); }\n"; } else { obj = "(new " + nested_type_name + "())"; } code += " public " + nested_type_name + lang_.optional_suffix + " "; - code += getNestedMethodName + "("; + code += get_nested_method_name + "("; if (lang_.language != IDLOptions::kCSharp) code += nested_type_name + " obj"; code += ") { int o = " + lang_.accessor_prefix + "__offset("; diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp index ff4cbf468..e27d664a8 100644 --- a/src/idl_gen_text.cpp +++ b/src/idl_gen_text.cpp @@ -165,6 +165,10 @@ template static bool GenField(const FieldDef &fd, opts, _text); } +static bool GenStruct(const StructDef &struct_def, const Table *table, + int indent, const IDLOptions &opts, + std::string *_text); + // Generate text for non-scalar field. static bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed, int indent, Type *union_type, @@ -180,6 +184,10 @@ static bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed, auto root = flexbuffers::GetRoot(vec->data(), vec->size()); root.ToString(true, opts.strict_json, *_text); return true; + } else if (fd.nested_flatbuffer) { + auto vec = table->GetPointer *>(fd.value.offset); + auto root = GetRoot(vec->data()); + return GenStruct(*fd.nested_flatbuffer, root, indent, opts, _text); } else { val = IsStruct(fd.value.type) ? table->GetStruct(fd.value.offset) diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index af3f93e33..fff35aabe 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -16,6 +16,7 @@ #include #include +#include #ifdef _WIN32 #if !defined(_USE_MATH_DEFINES) @@ -723,6 +724,10 @@ CheckedError Parser::ParseField(StructDef &struct_def) { // This will cause an error if the root type of the nested flatbuffer // wasn't defined elsewhere. LookupCreateStruct(nested->constant); + + // Keep a pointer to StructDef in FieldDef to simplify re-use later + auto nested_qualified_name = namespaces_.back()->GetFullyQualifiedName(nested->constant); + field->nested_flatbuffer = structs_.Lookup(nested_qualified_name); } if (field->attributes.Lookup("flexbuffer")) { @@ -941,6 +946,8 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value, builder.Finish(); auto off = parser->builder_.CreateVector(builder.GetBuffer()); val.constant = NumToString(off.o); + } else if (field->nested_flatbuffer) { + ECHECK(parser->ParseNestedFlatbuffer(val, field, fieldn, struct_def_inner)); } else { ECHECK(parser->ParseAnyValue(val, field, fieldn, struct_def_inner)); } @@ -1120,6 +1127,38 @@ CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue) { return NoError(); } +CheckedError Parser::ParseNestedFlatbuffer(Value &val, FieldDef *field, + size_t fieldn, + const StructDef *parent_struct_def) { + if (token_ == '[') {// backwards compat for 'legacy' ubyte buffers + ECHECK(ParseAnyValue(val, field, fieldn, parent_struct_def)); + } else { + auto cursor_at_value_begin = cursor_; + ECHECK(SkipAnyJsonValue()); + std::string substring(cursor_at_value_begin -1 , cursor_ -1); + + // Create and initialize new parser + Parser nested_parser; + assert(field->nested_flatbuffer); + nested_parser.root_struct_def_ = field->nested_flatbuffer; + nested_parser.enums_ = enums_; + nested_parser.opts = opts; + nested_parser.uses_flexbuffers_ = uses_flexbuffers_; + + // Parse JSON substring into new flatbuffer builder using nested_parser + if (!nested_parser.Parse(substring.c_str(), nullptr, nullptr)) { + ECHECK(Error(nested_parser.error_)); + } + auto off = builder_.CreateVector(nested_parser.builder_.GetBufferPointer(), nested_parser.builder_.GetSize()); + val.constant = NumToString(off.o); + + // Clean nested_parser before destruction to avoid deleting the elements in the SymbolTables + nested_parser.enums_.dict.clear(); + nested_parser.enums_.vec.clear(); + } + return NoError(); +} + CheckedError Parser::ParseMetaData(SymbolTable *attributes) { if (Is('(')) { NEXT();