Add flatbuffer to json using reflection schema
Change-Id: I6f32c0403a74ec951e6c730c4006ede3bc91099c
diff --git a/aos/flatbuffer_introspection.cc b/aos/flatbuffer_introspection.cc
new file mode 100644
index 0000000..751666d
--- /dev/null
+++ b/aos/flatbuffer_introspection.cc
@@ -0,0 +1,279 @@
+#include <iostream>
+#include <sstream>
+
+#include "aos/json_to_flatbuffer.h"
+
+namespace aos {
+
+namespace {
+
+using reflection::BaseType;
+
+void IntToString(int64_t val, reflection::BaseType type,
+ std::stringstream *out) {
+ switch (type) {
+ case BaseType::Bool:
+ *out << (val ? "true" : "false");
+ break;
+ case BaseType::UByte:
+ *out << std::to_string(static_cast<uint8_t>(val));
+ break;
+ case BaseType::Byte:
+ *out << std::to_string(static_cast<int8_t>(val));
+ break;
+ case BaseType::Short:
+ *out << static_cast<int16_t>(val);
+ break;
+ case BaseType::UShort:
+ *out << static_cast<uint16_t>(val);
+ break;
+ case BaseType::Int:
+ *out << static_cast<int32_t>(val);
+ break;
+ case BaseType::UInt:
+ *out << static_cast<uint32_t>(val);
+ break;
+ case BaseType::Long:
+ *out << static_cast<int64_t>(val);
+ break;
+ case BaseType::ULong:
+ *out << static_cast<uint64_t>(val);
+ break;
+ default:
+ *out << "null";
+ }
+}
+
+void FloatToString(double val, reflection::BaseType type,
+ std::stringstream *out) {
+ switch (type) {
+ case BaseType::Float:
+ out->precision(std::numeric_limits<float>::digits10);
+ *out << static_cast<float>(val);
+ break;
+ case BaseType::Double:
+ out->precision(std::numeric_limits<double>::digits10);
+ *out << val;
+ break;
+ default:
+ *out << "null";
+ }
+}
+
+template <typename ObjT>
+void ObjectToString(
+ const reflection::Object *obj,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
+ const ObjT *object, std::stringstream *out);
+
+// Get enum value name
+const char *EnumToString(
+ int64_t enum_value,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::EnumVal>>
+ *values) {
+ // Replace with binary search? Enum values are pre-sorted.
+ for (auto iter = values->begin(); iter != values->end(); iter++) {
+ if (enum_value == iter->value()) {
+ return iter->name()->c_str();
+ }
+ }
+ return nullptr;
+}
+
+// Convert integer to string, checking if it is an enum.
+void IntOrEnumToString(
+ int64_t val, const reflection::Type *type,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
+ std::stringstream *out) {
+ // Check if integer is an enum and print string, otherwise fallback to
+ // printing as int.
+ if (type->index() > -1 && type->index() < (int32_t)enums->size()) {
+ const reflection::Enum *enum_props = enums->Get(type->index());
+ if (!enum_props->is_union()) {
+ const char *value_string = EnumToString(val, enum_props->values());
+
+ if (value_string != nullptr) {
+ *out << '"' << value_string << '"';
+ }
+ }
+ } else {
+ if (type->base_type() == BaseType::Vector ||
+ type->base_type() == BaseType::Array) {
+ IntToString(val, type->element(), out);
+ } else {
+ IntToString(val, type->base_type(), out);
+ }
+ }
+}
+
+// Print field in flatbuffer table. Field must be populated.
+template <typename ObjT>
+void FieldToString(
+ const ObjT *table, const reflection::Field *field,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
+ std::stringstream *out) {
+ const reflection::Type *type = field->type();
+
+ switch (type->base_type()) {
+ case BaseType::Bool:
+ case BaseType::UByte:
+ case BaseType::Byte:
+ case BaseType::Short:
+ case BaseType::UShort:
+ case BaseType::Int:
+ case BaseType::UInt:
+ case BaseType::Long:
+ case BaseType::ULong:
+ IntOrEnumToString(GetAnyFieldI(*table, *field), type, enums, out);
+ break;
+ case BaseType::Float:
+ case BaseType::Double:
+ FloatToString(GetAnyFieldF(*table, *field), type->base_type(), out);
+ break;
+ case BaseType::String:
+ if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
+ std::string str = flatbuffers::GetFieldS(*table, *field)->str();
+ std::string out_str;
+ out_str.reserve(str.size());
+ for (char c : str) {
+ // out_str += c;
+ switch (c) {
+ case '"':
+ out_str += "\\\"";
+ break;
+ case '\\':
+ out_str += "\\\\";
+ break;
+ case '\b':
+ out_str += "\\b";
+ break;
+ case '\f':
+ out_str += "\\f";
+ break;
+ case '\n':
+ out_str += "\\n";
+ break;
+ case '\r':
+ out_str += "\\r";
+ break;
+ case '\t':
+ out_str += "\\t";
+ break;
+ default:
+ out_str += c;
+ }
+ }
+ *out << '"' << out_str << '"';
+ } else {
+ *out << "null";
+ }
+ break;
+ case BaseType::Vector: {
+ if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
+ const flatbuffers::VectorOfAny *vector =
+ flatbuffers::GetFieldAnyV(*table, *field);
+ reflection::BaseType elem_type = type->element();
+
+ *out << '[';
+ for (flatbuffers::uoffset_t i = 0; i < vector->size(); ++i) {
+ if (i != 0) {
+ *out << ", ";
+ }
+ if (flatbuffers::IsInteger(elem_type)) {
+ IntOrEnumToString(
+ flatbuffers::GetAnyVectorElemI(vector, elem_type, i), type,
+ enums, out);
+ } else if (flatbuffers::IsFloat(elem_type)) {
+ FloatToString(flatbuffers::GetAnyVectorElemF(vector, elem_type, i),
+ elem_type, out);
+ } else if (elem_type == BaseType::String) {
+ *out << '"' << flatbuffers::GetAnyVectorElemS(vector, elem_type, i)
+ << '"';
+ } else if (elem_type == BaseType::Obj) {
+ if (type->index() > -1 &&
+ type->index() < (int32_t)objects->size()) {
+ if (objects->Get(type->index())->is_struct()) {
+ ObjectToString(
+ objects->Get(type->index()), objects, enums,
+ flatbuffers::GetAnyVectorElemAddressOf<
+ const flatbuffers::Struct>(
+ vector, i, objects->Get(type->index())->bytesize()),
+ out);
+ } else {
+ ObjectToString(objects->Get(type->index()), objects, enums,
+ flatbuffers::GetAnyVectorElemPointer<
+ const flatbuffers::Table>(vector, i),
+ out);
+ }
+ }
+ }
+ }
+ *out << ']';
+ } else {
+ *out << "null";
+ }
+ } break;
+ case BaseType::Obj: {
+ if (type->index() > -1 && type->index() < (int32_t)objects->size()) {
+ if (objects->Get(type->index())->is_struct()) {
+ ObjectToString(objects->Get(type->index()), objects, enums,
+ flatbuffers::GetFieldStruct(*table, *field), out);
+ } else if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
+ ObjectToString(objects->Get(type->index()), objects, enums,
+ flatbuffers::GetFieldT(*table, *field), out);
+ }
+ } else {
+ *out << "null";
+ }
+ } break;
+ default:
+ *out << "null";
+ }
+}
+
+// Prints flatbuffer table or struct given list of possible child objects and
+// enums. Prints "null" if the child object type is not found.
+template <typename ObjT>
+void ObjectToString(
+ const reflection::Object *obj,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
+ const ObjT *object, std::stringstream *out) {
+ static_assert(std::is_same<flatbuffers::Table, ObjT>() ||
+ std::is_same<flatbuffers::Struct, ObjT>(),
+ "Type must be either flatbuffer table or struct");
+ bool print_sep = false;
+ *out << '{';
+ for (const reflection::Field *field : *obj->fields()) {
+ // Check whether this object has the field populated (even for structs,
+ // which should have all fields populated)
+ if (object->GetAddressOf(field->offset())) {
+ if (print_sep) {
+ *out << ", ";
+ } else {
+ print_sep = true;
+ }
+ *out << '"' << field->name()->c_str() << "\": ";
+ FieldToString(object, field, objects, enums, out);
+ }
+ }
+ *out << '}';
+}
+
+} // namespace
+
+std::string FlatbufferToJson(const reflection::Schema *schema,
+ const uint8_t *data) {
+ const flatbuffers::Table *table = flatbuffers::GetAnyRoot(data);
+
+ const reflection::Object *obj = schema->root_table();
+
+ std::stringstream out;
+
+ ObjectToString(obj, schema->objects(), schema->enums(), table, &out);
+
+ return out.str();
+}
+} // namespace aos