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> *>(prev_val +
146 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);
209 FLATBUFFERS_ASSERT(0);
214 template<typename T> static T GetFieldDefault(const FieldDef &fd) {
216 auto check = StringToNumber(fd.value.constant.c_str(), &val);
218 FLATBUFFERS_ASSERT(check);
222 // Generate text for a scalar field.
224 static bool GenField(const FieldDef &fd, const Table *table, bool fixed,
225 const IDLOptions &opts, int indent, std::string *_text) {
227 fixed ? reinterpret_cast<const Struct *>(table)->GetField<T>(
229 : table->GetField<T>(fd.value.offset, GetFieldDefault<T>(fd)),
230 fd.value.type, indent, nullptr, -1, opts, _text);
233 static bool GenStruct(const StructDef &struct_def, const Table *table,
234 int indent, const IDLOptions &opts, std::string *_text);
236 // Generate text for non-scalar field.
237 static bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed,
238 int indent, const uint8_t *prev_val,
239 const IDLOptions &opts, std::string *_text) {
240 const void *val = nullptr;
242 // The only non-scalar fields in structs are structs or arrays.
243 FLATBUFFERS_ASSERT(IsStruct(fd.value.type) || IsArray(fd.value.type));
244 val = reinterpret_cast<const Struct *>(table)->GetStruct<const void *>(
246 } else if (fd.flexbuffer) {
247 auto vec = table->GetPointer<const Vector<uint8_t> *>(fd.value.offset);
248 auto root = flexbuffers::GetRoot(vec->data(), vec->size());
249 root.ToString(true, opts.strict_json, *_text);
251 } else if (fd.nested_flatbuffer) {
252 auto vec = table->GetPointer<const Vector<uint8_t> *>(fd.value.offset);
253 auto root = GetRoot<Table>(vec->data());
254 return GenStruct(*fd.nested_flatbuffer, root, indent, opts, _text);
256 val = IsStruct(fd.value.type)
257 ? table->GetStruct<const void *>(fd.value.offset)
258 : table->GetPointer<const void *>(fd.value.offset);
260 return Print(val, fd.value.type, indent, prev_val, -1, opts, _text);
263 // Generate text for a struct or table, values separated by commas, indented,
264 // and bracketed by "{}"
265 static bool GenStruct(const StructDef &struct_def, const Table *table,
266 int indent, const IDLOptions &opts, std::string *_text) {
267 std::string &text = *_text;
270 const uint8_t *prev_val = nullptr;
271 for (auto it = struct_def.fields.vec.begin();
272 it != struct_def.fields.vec.end(); ++it) {
274 auto is_present = struct_def.fixed || table->CheckField(fd.value.offset);
275 auto output_anyway = opts.output_default_scalars_in_json &&
276 IsScalar(fd.value.type.base_type) && !fd.deprecated;
277 if (is_present || output_anyway) {
279 if (!opts.protobuf_ascii_alike) text += ",";
281 text += NewLine(opts);
282 text.append(indent + Indent(opts), ' ');
283 OutputIdentifier(fd.name, opts, _text);
284 if (!opts.protobuf_ascii_alike ||
285 (fd.value.type.base_type != BASE_TYPE_STRUCT &&
286 fd.value.type.base_type != BASE_TYPE_VECTOR))
289 switch (fd.value.type.base_type) {
291 #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
292 CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
293 case BASE_TYPE_ ## ENUM: \
294 if (!GenField<CTYPE>(fd, table, struct_def.fixed, \
295 opts, indent + Indent(opts), _text)) { \
299 FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
300 #undef FLATBUFFERS_TD
301 // Generate drop-thru case statements for all pointer types:
302 #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
303 CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
304 case BASE_TYPE_ ## ENUM:
305 FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
306 FLATBUFFERS_GEN_TYPE_ARRAY(FLATBUFFERS_TD)
307 #undef FLATBUFFERS_TD
308 if (!GenFieldOffset(fd, table, struct_def.fixed, indent + Indent(opts),
309 prev_val, opts, _text)) {
315 // Track prev val for use with union types.
316 if (struct_def.fixed) {
317 prev_val = reinterpret_cast<const uint8_t *>(table) + fd.value.offset;
319 prev_val = table->GetAddressOf(fd.value.offset);
323 text += NewLine(opts);
324 text.append(indent, ' ');
329 // Generate a text representation of a flatbuffer in JSON format.
330 bool GenerateTextFromTable(const Parser &parser, const void *table,
331 const std::string &table_name, std::string *_text) {
332 auto struct_def = parser.LookupStruct(table_name);
333 if (struct_def == nullptr) {
337 text.reserve(1024); // Reduce amount of inevitable reallocs.
338 auto root = static_cast<const Table *>(table);
339 if (!GenStruct(*struct_def, root, 0, parser.opts, &text)) {
342 text += NewLine(parser.opts);
346 // Generate a text representation of a flatbuffer in JSON format.
347 bool GenerateText(const Parser &parser, const void *flatbuffer,
348 std::string *_text) {
349 std::string &text = *_text;
350 FLATBUFFERS_ASSERT(parser.root_struct_def_); // call SetRootType()
351 text.reserve(1024); // Reduce amount of inevitable reallocs.
352 auto root = parser.opts.size_prefixed ?
353 GetSizePrefixedRoot<Table>(flatbuffer) : GetRoot<Table>(flatbuffer);
354 if (!GenStruct(*parser.root_struct_def_, root, 0, parser.opts, _text)) {
357 text += NewLine(parser.opts);
361 std::string TextFileName(const std::string &path,
362 const std::string &file_name) {
363 return path + file_name + ".json";
366 bool GenerateTextFile(const Parser &parser, const std::string &path,
367 const std::string &file_name) {
368 if (parser.opts.use_flexbuffers) {
370 parser.flex_root_.ToString(true, parser.opts.strict_json, json);
371 return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(),
372 json.c_str(), json.size(), true);
374 if (!parser.builder_.GetSize() || !parser.root_struct_def_) return true;
376 if (!GenerateText(parser, parser.builder_.GetBufferPointer(), &text)) {
379 return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(), text,
383 std::string TextMakeRule(const Parser &parser, const std::string &path,
384 const std::string &file_name) {
385 if (!parser.builder_.GetSize() || !parser.root_struct_def_) return "";
386 std::string filebase =
387 flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
388 std::string make_rule = TextFileName(path, filebase) + ": " + file_name;
389 auto included_files =
390 parser.GetIncludedFilesRecursive(parser.root_struct_def_->file);
391 for (auto it = included_files.begin(); it != included_files.end(); ++it) {
392 make_rule += " " + *it;
397 } // namespace flatbuffers