2 * Copyright 2014 Google Inc. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 // independent from idl_parser, since this code is not needed for most clients
19 #include "flatbuffers/flatbuffers.h"
20 #include "flatbuffers/flexbuffers.h"
21 #include "flatbuffers/idl.h"
22 #include "flatbuffers/util.h"
24 namespace flatbuffers {
26 static bool GenStruct(const StructDef &struct_def, const Table *table,
27 int indent, const IDLOptions &opts, std::string *_text);
29 // If indentation is less than 0, that indicates we don't want any newlines
31 const char *NewLine(const IDLOptions &opts) {
32 return opts.indent_step >= 0 ? "\n" : "";
35 int Indent(const IDLOptions &opts) { return std::max(opts.indent_step, 0); }
37 // Output an identifier with or without quotes depending on strictness.
38 void OutputIdentifier(const std::string &name, const IDLOptions &opts,
40 std::string &text = *_text;
41 if (opts.strict_json) text += "\"";
43 if (opts.strict_json) text += "\"";
46 // Print (and its template specialization below for pointers) generate text
47 // for a single FlatBuffer value into JSON format.
48 // The general case for scalars:
50 bool Print(T val, Type type, int /*indent*/, const uint8_t * /*prev_val*/,
51 soffset_t /*vector_index*/, const IDLOptions &opts,
53 std::string &text = *_text;
54 if (type.enum_def && opts.output_enum_identifiers) {
55 std::vector<EnumVal const *> enum_values;
56 if (auto ev = type.enum_def->ReverseLookup(static_cast<int64_t>(val))) {
57 enum_values.push_back(ev);
58 } else if (val && type.enum_def->attributes.Lookup("bit_flags")) {
59 for (auto it = type.enum_def->Vals().begin(),
60 e = type.enum_def->Vals().end();
62 if ((*it)->GetAsUInt64() & static_cast<uint64_t>(val))
63 enum_values.push_back(*it);
66 if (!enum_values.empty()) {
68 for (auto it = enum_values.begin(), e = enum_values.end(); it != e; ++it)
69 text += (*it)->name + ' ';
70 text[text.length() - 1] = '\"';
75 if (type.base_type == BASE_TYPE_BOOL) {
76 text += val != 0 ? "true" : "false";
78 text += NumToString(val);
84 // Print a vector or an array of JSON values, comma seperated, wrapped in "[]".
85 template<typename T, typename Container>
86 bool PrintContainer(const Container &c, size_t size, Type type, int indent,
87 const uint8_t *prev_val, const IDLOptions &opts,
89 std::string &text = *_text;
91 text += NewLine(opts);
92 for (uoffset_t i = 0; i < size; i++) {
94 if (!opts.protobuf_ascii_alike) text += ",";
95 text += NewLine(opts);
97 text.append(indent + Indent(opts), ' ');
99 if (!Print(reinterpret_cast<const void *>(c.Data() +
100 i * type.struct_def->bytesize),
101 type, indent + Indent(opts), nullptr, -1, opts, _text)) {
105 if (!Print(c[i], type, indent + Indent(opts), prev_val,
106 static_cast<soffset_t>(i), opts, _text)) {
111 text += NewLine(opts);
112 text.append(indent, ' ');
118 bool PrintVector(const Vector<T> &v, Type type, int indent,
119 const uint8_t *prev_val, const IDLOptions &opts,
120 std::string *_text) {
121 return PrintContainer<T, Vector<T>>(v, v.size(), type, indent, prev_val, opts,
125 // Print an array a sequence of JSON values, comma separated, wrapped in "[]".
127 bool PrintArray(const Array<T, 0xFFFF> &a, size_t size, Type type, int indent,
128 const IDLOptions &opts, std::string *_text) {
129 return PrintContainer<T, Array<T, 0xFFFF>>(a, size, type, indent, nullptr,
133 // Specialization of Print above for pointer types.
135 bool Print<const void *>(const void *val, Type type, int indent,
136 const uint8_t *prev_val, soffset_t vector_index,
137 const IDLOptions &opts, std::string *_text) {
138 switch (type.base_type) {
139 case BASE_TYPE_UNION: {
140 // If this assert hits, you have an corrupt buffer, a union type field
141 // was not present or was out of range.
142 FLATBUFFERS_ASSERT(prev_val);
143 auto union_type_byte = *prev_val; // Always a uint8_t.
144 if (vector_index >= 0) {
145 auto type_vec = reinterpret_cast<const Vector<uint8_t> *>(
146 prev_val + ReadScalar<uoffset_t>(prev_val));
147 union_type_byte = type_vec->Get(static_cast<uoffset_t>(vector_index));
149 auto enum_val = type.enum_def->ReverseLookup(union_type_byte, true);
151 return Print<const void *>(val, enum_val->union_type, indent, nullptr,
157 case BASE_TYPE_STRUCT:
158 return GenStruct(*type.struct_def, reinterpret_cast<const Table *>(val),
159 indent, opts, _text);
160 case BASE_TYPE_STRING: {
161 auto s = reinterpret_cast<const String *>(val);
162 return EscapeString(s->c_str(), s->size(), _text, opts.allow_non_utf8,
165 case BASE_TYPE_VECTOR: {
166 const auto vec_type = type.VectorType();
167 // Call PrintVector above specifically for each element type:
169 switch (vec_type.base_type) {
170 #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
171 CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
172 case BASE_TYPE_ ## ENUM: \
173 if (!PrintVector<CTYPE>( \
174 *reinterpret_cast<const Vector<CTYPE> *>(val), \
175 vec_type, indent, prev_val, opts, _text)) { \
179 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
180 #undef FLATBUFFERS_TD
185 case BASE_TYPE_ARRAY: {
186 const auto vec_type = type.VectorType();
187 // Call PrintArray above specifically for each element type:
189 switch (vec_type.base_type) {
190 #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
191 CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
192 case BASE_TYPE_ ## ENUM: \
193 if (!PrintArray<CTYPE>( \
194 *reinterpret_cast<const Array<CTYPE, 0xFFFF> *>(val), \
196 vec_type, indent, opts, _text)) { \
200 FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
201 FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
202 #undef FLATBUFFERS_TD
203 case BASE_TYPE_ARRAY: FLATBUFFERS_ASSERT(0);
208 default: FLATBUFFERS_ASSERT(0); return false;
212 template<typename T> static T GetFieldDefault(const FieldDef &fd) {
214 auto check = StringToNumber(fd.value.constant.c_str(), &val);
216 FLATBUFFERS_ASSERT(check);
220 // Generate text for a scalar field.
222 static bool GenField(const FieldDef &fd, const Table *table, bool fixed,
223 const IDLOptions &opts, int indent, std::string *_text) {
225 fixed ? reinterpret_cast<const Struct *>(table)->GetField<T>(
227 : table->GetField<T>(fd.value.offset, GetFieldDefault<T>(fd)),
228 fd.value.type, indent, nullptr, -1, opts, _text);
231 static bool GenStruct(const StructDef &struct_def, const Table *table,
232 int indent, const IDLOptions &opts, std::string *_text);
234 // Generate text for non-scalar field.
235 static bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed,
236 int indent, const uint8_t *prev_val,
237 const IDLOptions &opts, std::string *_text) {
238 const void *val = nullptr;
240 // The only non-scalar fields in structs are structs or arrays.
241 FLATBUFFERS_ASSERT(IsStruct(fd.value.type) || IsArray(fd.value.type));
242 val = reinterpret_cast<const Struct *>(table)->GetStruct<const void *>(
244 } else if (fd.flexbuffer) {
245 auto vec = table->GetPointer<const Vector<uint8_t> *>(fd.value.offset);
246 auto root = flexbuffers::GetRoot(vec->data(), vec->size());
247 root.ToString(true, opts.strict_json, *_text);
249 } else if (fd.nested_flatbuffer) {
250 auto vec = table->GetPointer<const Vector<uint8_t> *>(fd.value.offset);
251 auto root = GetRoot<Table>(vec->data());
252 return GenStruct(*fd.nested_flatbuffer, root, indent, opts, _text);
254 val = IsStruct(fd.value.type)
255 ? table->GetStruct<const void *>(fd.value.offset)
256 : table->GetPointer<const void *>(fd.value.offset);
258 return Print(val, fd.value.type, indent, prev_val, -1, opts, _text);
261 // Generate text for a struct or table, values separated by commas, indented,
262 // and bracketed by "{}"
263 static bool GenStruct(const StructDef &struct_def, const Table *table,
264 int indent, const IDLOptions &opts, std::string *_text) {
265 std::string &text = *_text;
268 const uint8_t *prev_val = nullptr;
269 for (auto it = struct_def.fields.vec.begin();
270 it != struct_def.fields.vec.end(); ++it) {
272 auto is_present = struct_def.fixed || table->CheckField(fd.value.offset);
273 auto output_anyway = opts.output_default_scalars_in_json &&
274 IsScalar(fd.value.type.base_type) && !fd.deprecated;
275 if (is_present || output_anyway) {
277 if (!opts.protobuf_ascii_alike) text += ",";
279 text += NewLine(opts);
280 text.append(indent + Indent(opts), ' ');
281 OutputIdentifier(fd.name, opts, _text);
282 if (!opts.protobuf_ascii_alike ||
283 (fd.value.type.base_type != BASE_TYPE_STRUCT &&
284 fd.value.type.base_type != BASE_TYPE_VECTOR))
287 switch (fd.value.type.base_type) {
289 #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
290 CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
291 case BASE_TYPE_ ## ENUM: \
292 if (!GenField<CTYPE>(fd, table, struct_def.fixed, \
293 opts, indent + Indent(opts), _text)) { \
297 FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
298 #undef FLATBUFFERS_TD
299 // Generate drop-thru case statements for all pointer types:
300 #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
301 CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
302 case BASE_TYPE_ ## ENUM:
303 FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
304 FLATBUFFERS_GEN_TYPE_ARRAY(FLATBUFFERS_TD)
305 #undef FLATBUFFERS_TD
306 if (!GenFieldOffset(fd, table, struct_def.fixed, indent + Indent(opts),
307 prev_val, opts, _text)) {
313 // Track prev val for use with union types.
314 if (struct_def.fixed) {
315 prev_val = reinterpret_cast<const uint8_t *>(table) + fd.value.offset;
317 prev_val = table->GetAddressOf(fd.value.offset);
321 text += NewLine(opts);
322 text.append(indent, ' ');
327 // Generate a text representation of a flatbuffer in JSON format.
328 bool GenerateTextFromTable(const Parser &parser, const void *table,
329 const std::string &table_name, std::string *_text) {
330 auto struct_def = parser.LookupStruct(table_name);
331 if (struct_def == nullptr) { return false; }
333 text.reserve(1024); // Reduce amount of inevitable reallocs.
334 auto root = static_cast<const Table *>(table);
335 if (!GenStruct(*struct_def, root, 0, parser.opts, &text)) { return false; }
336 text += NewLine(parser.opts);
340 // Generate a text representation of a flatbuffer in JSON format.
341 bool GenerateText(const Parser &parser, const void *flatbuffer,
342 std::string *_text) {
343 std::string &text = *_text;
344 FLATBUFFERS_ASSERT(parser.root_struct_def_); // call SetRootType()
345 text.reserve(1024); // Reduce amount of inevitable reallocs.
346 auto root = parser.opts.size_prefixed ? GetSizePrefixedRoot<Table>(flatbuffer)
347 : GetRoot<Table>(flatbuffer);
348 if (!GenStruct(*parser.root_struct_def_, root, 0, parser.opts, _text)) {
351 text += NewLine(parser.opts);
355 std::string TextFileName(const std::string &path,
356 const std::string &file_name) {
357 return path + file_name + ".json";
360 bool GenerateTextFile(const Parser &parser, const std::string &path,
361 const std::string &file_name) {
362 if (parser.opts.use_flexbuffers) {
364 parser.flex_root_.ToString(true, parser.opts.strict_json, json);
365 return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(),
366 json.c_str(), json.size(), true);
368 if (!parser.builder_.GetSize() || !parser.root_struct_def_) return true;
370 if (!GenerateText(parser, parser.builder_.GetBufferPointer(), &text)) {
373 return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(), text,
377 std::string TextMakeRule(const Parser &parser, const std::string &path,
378 const std::string &file_name) {
379 if (!parser.builder_.GetSize() || !parser.root_struct_def_) return "";
380 std::string filebase =
381 flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
382 std::string make_rule = TextFileName(path, filebase) + ": " + file_name;
383 auto included_files =
384 parser.GetIncludedFilesRecursive(parser.root_struct_def_->file);
385 for (auto it = included_files.begin(); it != included_files.end(); ++it) {
386 make_rule += " " + *it;
391 } // namespace flatbuffers