This tutorial provides a basic example of how to work with [FlatBuffers](@ref flatbuffers_overview). We will step through a simple example application, which shows you how to:
schema
file.flatc
FlatBuffer compiler.During this example, imagine that you are creating a game where the
main character, the hero of the story, needs to slay some
orc
s. We will walk through each step necessary to create
this monster type using FlatBuffers.
Please select your desired language for our quest: \htmlonly
\endhtmlonly\htmlonly
\endhtmlonly
Samples demonstating the concepts in this example are located in the
source code package, under the samples
directory. You can
browse the samples on GitHub here.
For your chosen language, please cross-reference with:
To start working with FlatBuffers, you first need to create a
schema
file, which defines the format for each data
structure you wish to serialize. Here is the schema
that
defines the template for our monsters:
// Example IDL file for our monster's schema.
namespace MyGame.Sample;
enum Color:byte { Red = 0, Green, Blue = 2 }
union Equipment { Weapon } // Optionally add more tables.
struct Vec3 {
x:float;
y:float;
z:float;
}
table Monster {
pos:Vec3; // Struct.
mana:short = 150;
hp:short = 100;
name:string;
friendly:bool = false (deprecated);
inventory:[ubyte]; // Vector of scalars.
color:Color = Blue; // Enum.
weapons:[Weapon]; // Vector of tables.
equipped:Equipment; // Union.
path:[Vec3]; // Vector of structs.
}
table Weapon {
name:string;
damage:short;
}
root_type Monster;
As you can see, the syntax for the schema
Interface
Definition Language (IDL) is similar to those of the C family of
languages, and other IDL languages. Let's examine each part of this
schema
to determine what it does.
The schema
starts with a namespace
declaration. This determines the corresponding package/namespace for the
generated code. In our example, we have the Sample
namespace inside of the MyGame
namespace.
Next, we have an enum
definition. In this example, we
have an enum
of type byte
, named
Color
. We have three values in this enum
:
Red
, Green
, and Blue
. We specify
Red = 0
and Blue = 2
, but we do not specify an
explicit value for Green
. Since the behavior of an
enum
is to increment if unspecified, Green
will receive the implicit value of 1
.
Following the enum
is a union
. The
union
in this example is not very useful, as it only
contains the one table
(named Weapon
). If we
had created multiple tables that we would want the union
to
be able to reference, we could add more elements to the
union Equipment
.
After the union
comes a struct Vec3
, which
represents a floating point vector with 3
dimensions. We
use a struct
here, over a table
, because
struct
s are ideal for data structures that will not change,
since they use less memory and have faster lookup.
The Monster
table is the main object in our FlatBuffer.
This will be used as the template to store our orc
monster.
We specify some default values for fields, such as
mana:short = 150
. If unspecified, scalar fields (like
int
, uint
, or float
) will be
given a default of 0
while strings and tables will be given
a default of null
. Another thing to note is the line
friendly:bool = false (deprecated);
. Since you cannot
delete fields from a table
(to support backwards
compatibility), you can set fields as deprecated
, which
will prevent the generation of accessors for this field in the generated
code. Be careful when using deprecated
, however, as it may
break legacy code that used this accessor.
The Weapon
table is a sub-table used within our
FlatBuffer. It is used twice: once within the Monster
table
and once within the Equipment
union. For our
Monster
, it is used to populate a
vector of tables
via the weapons
field within
our Monster
. It is also the only table referenced by the
Equipment
union.
The last part of the schema
is the
root_type
. The root type declares what will be the root
table for the serialized data. In our case, the root type is our
Monster
table.
The scalar types can also use alias type names such as
int16
instead of short
and
float32
instead of float
. Thus we could also
write the Weapon
table as:
table Weapon {
name:string;
damage:int16;
}
You can find a complete guide to writing schema
files in
the [Writing a schema](@ref flatbuffers_guide_writing_schema) section of
the Programmer's Guide. You can also view the formal [Grammar of the
schema language](@ref flatbuffers_grammar).
After you have written the FlatBuffers schema, the next step is to compile it.
If you have not already done so, please follow [these
instructions](@ref flatbuffers_guide_building) to build
flatc
, the FlatBuffer compiler.
Once flatc
is built successfully, compile the schema for
your language:
For a more complete guide to using the flatc
compiler,
please read the [Using the schema compiler](@ref
flatbuffers_guide_using_schema_compiler) section of the Programmer's
Guide.
Now that we have compiled the schema for our programming language, we can start creating some monsters and serializing/deserializing them from FlatBuffers.
The first step is to import/include the library, generated files, etc.
using namespace MyGame::Sample; // Specified in the schema.
</div>
<div class="language-java">
~~~{.java}
import MyGame.Sample.*; //The `flatc` generated files. (Monster, Vec3, etc.)
import com.google.flatbuffers.FlatBufferBuilder;
import com.google.flatbuffers.FlatBufferBuilder
</div>
<div class="language-csharp">
~~~{.cs}
using Google.FlatBuffers;
using MyGame.Sample; // The `flatc` generated files. (Monster, Vec3, etc.)
flatc
.import MyGame.Sample.Color import MyGame.Sample.Equipment import MyGame.Sample.Monster import MyGame.Sample.Vec3 import MyGame.Sample.Weapon
</div>
<div class="language-javascript">
~~~{.js}
// The following code is an example - use your desired module flavor by transpiling from TS.
var flatbuffers = require('/js/flatbuffers').flatbuffers;
var MyGame = require('./monster_generated').MyGame; // Generated by `flatc`.
//--------------------------------------------------------------------------//
// The following code is for browser-based HTML/JavaScript. Use the above code
// for JavaScript module loaders (e.g. Node.js).
<script src="../js/flatbuffers.js"></script>
<script src="monster_generated.js"></script> // Generated by `flatc`.
import { MyGame } from './monster_generated';
</div>
<div class="language-php">
~~~{.php}
// It is recommended that your use PSR autoload when using FlatBuffers in PHP.
// Here is an example from `SampleBinary.php`:
function __autoload($class_name) {
// The last segment of the class name matches the file name.
$class = substr($class_name, strrpos($class_name, "\\") + 1);
$root_dir = join(DIRECTORY_SEPARATOR, array(dirname(dirname(__FILE__)))); // `flatbuffers` root.
// Contains the `*.php` files for the FlatBuffers library and the `flatc` generated files.
$paths = array(join(DIRECTORY_SEPARATOR, array($root_dir, "php")),
join(DIRECTORY_SEPARATOR, array($root_dir, "samples", "MyGame", "Sample")));
foreach ($paths as $path) {
$file = join(DIRECTORY_SEPARATOR, array($path, $class . ".php"));
if (file_exists($file)) {
require($file);
break;
}
}
}
// Convenient namespace macro to manage long namespace prefix. #undef ns #define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) // Specified in the schema.
// A helper to simplify creating vectors from C-arrays. #define c_vec_len(V) (sizeof(V)/sizeof((V)[0]))
</div>
<div class="language-dart">
~~~{.dart}
import 'package:flat_buffers/flat_buffers.dart' as fb;
// Generated by `flatc`.
import 'monster_my_game.sample_generated.dart' as myGame;
-- require the generated files from flatc
. local color =
require("MyGame.Sample.Color") local equipment =
require("MyGame.Sample.Equipment") local monster =
require("MyGame.Sample.Monster") local vec3 =
require("MyGame.Sample.Vec3") local weapon =
require("MyGame.Sample.Weapon")
</div>
<div class="language-lobster">
~~~{.lobster}
import from "../lobster/" // Where to find flatbuffers.lobster
import monster_generated
// import the generated code #[allow(dead_code, unused_imports)] #[path = "./monster_generated.rs"] mod monster_generated; pub use monster_generated::my_game::sample::{get_root_as_monster, Color, Equipment, Monster, MonsterArgs, Vec3, Weapon, WeaponArgs};
</div>
<div class="language-swift">
~~~{.swift}
/**
// make sure that monster_generated.swift is included in your project
*/
import Flatbuffers
// typealiases for convenience
typealias Monster = MyGame1_Sample_Monster
typealias Weapon = MyGame1_Sample_Weapon
typealias Color = MyGame1_Sample_Color
typealias Vec3 = MyGame1_Sample_Vec3
Now we are ready to start building some buffers. In order to start,
we need to create an instance of the FlatBufferBuilder
,
which will contain the buffer as it grows. You can pass an initial size
of the buffer (here 1024 bytes), which will grow automatically if
needed:
After creating the builder
, we can start serializing our
data. Before we make our orc
Monster, let's create some
Weapon
s: a Sword
and an Axe
.
auto weapon_two_name = builder.CreateString("Axe"); short weapon_two_damage = 5;
// Use the CreateWeapon
shortcut to create Weapons with
all the fields set. auto sword = CreateWeapon(builder, weapon_one_name,
weapon_one_damage); auto axe = CreateWeapon(builder, weapon_two_name,
weapon_two_damage);
</div>
<div class="language-java">
~~~{.java}
int weaponOneName = builder.createString("Sword")
short weaponOneDamage = 3;
int weaponTwoName = builder.createString("Axe");
short weaponTwoDamage = 5;
// Use the `createWeapon()` helper function to create the weapons, since we set every field.
int sword = Weapon.createWeapon(builder, weaponOneName, weaponOneDamage);
int axe = Weapon.createWeapon(builder, weaponTwoName, weaponTwoDamage);
val weaponTwoName = builder.createString("Axe") val weaponTwoDamage: Short = 5;
// Use the createWeapon()
helper function to create the
weapons, since we set every field. val sword =
Weapon.createWeapon(builder, weaponOneName, weaponOneDamage) val axe =
Weapon.createWeapon(builder, weaponTwoName, weaponTwoDamage)
</div>
<div class="language-csharp">
~~~{.cs}
var weaponOneName = builder.CreateString("Sword");
var weaponOneDamage = 3;
var weaponTwoName = builder.CreateString("Axe");
var weaponTwoDamage = 5;
// Use the `CreateWeapon()` helper function to create the weapons, since we set every field.
var sword = Weapon.CreateWeapon(builder, weaponOneName, (short)weaponOneDamage);
var axe = Weapon.CreateWeapon(builder, weaponTwoName, (short)weaponTwoDamage);
// Create the first Weapon
("Sword").
sample.WeaponStart(builder) sample.WeaponAddName(builder, weaponOne)
sample.WeaponAddDamage(builder, 3) sword :=
sample.WeaponEnd(builder)
// Create the second Weapon
("Axe").
sample.WeaponStart(builder) sample.WeaponAddName(builder, weaponTwo)
sample.WeaponAddDamage(builder, 5) axe := sample.WeaponEnd(builder)
</div>
<div class="language-python">
~~~{.py}
weapon_one = builder.CreateString('Sword')
weapon_two = builder.CreateString('Axe')
# Create the first `Weapon` ('Sword').
MyGame.Sample.Weapon.Start(builder)
MyGame.Sample.Weapon.AddName(builder, weapon_one)
MyGame.Sample.Weapon.AddDamage(builder, 3)
sword = MyGame.Sample.Weapon.End(builder)
# Create the second `Weapon` ('Axe').
MyGame.Sample.Weapon.Start(builder)
MyGame.Sample.Weapon.AddName(builder, weapon_two)
MyGame.Sample.Weapon.AddDamage(builder, 5)
axe = MyGame.Sample.Weapon.End(builder)
// Create the first Weapon
('Sword').
MyGame.Sample.Weapon.startWeapon(builder);
MyGame.Sample.Weapon.addName(builder, weaponOne);
MyGame.Sample.Weapon.addDamage(builder, 3); var sword =
MyGame.Sample.Weapon.endWeapon(builder);
// Create the second Weapon
('Axe').
MyGame.Sample.Weapon.startWeapon(builder);
MyGame.Sample.Weapon.addName(builder, weaponTwo);
MyGame.Sample.Weapon.addDamage(builder, 5); var axe =
MyGame.Sample.Weapon.endWeapon(builder);
</div>
<div class="language-typescript">
~~~{.ts}
let weaponOne = builder.createString('Sword');
let weaponTwo = builder.createString('Axe');
// Create the first `Weapon` ('Sword').
MyGame.Sample.Weapon.startWeapon(builder);
MyGame.Sample.Weapon.addName(builder, weaponOne);
MyGame.Sample.Weapon.addDamage(builder, 3);
let sword = MyGame.Sample.Weapon.endWeapon(builder);
// Create the second `Weapon` ('Axe').
MyGame.Sample.Weapon.startWeapon(builder);
MyGame.Sample.Weapon.addName(builder, weaponTwo);
MyGame.Sample.Weapon.addDamage(builder, 5);
let axe = MyGame.Sample.Weapon.endWeapon(builder);
$weapon_two_name = $builder->createString("Axe"); $axe = \MyGame\Sample\Weapon::CreateWeapon($builder, $weapon_two_name, 5);
// Create an array from the two Weapon
s and pass it to
the // CreateWeaponsVector()
method to create a FlatBuffer
vector. weaps = array(sword,
axe);weapons
= \MyGame\Sample\Monster::CreateWeaponsVector($builder, $weaps);
</div>
<div class="language-c">
~~~{.c}
flatbuffers_string_ref_t weapon_one_name = flatbuffers_string_create_str(B, "Sword");
uint16_t weapon_one_damage = 3;
flatbuffers_string_ref_t weapon_two_name = flatbuffers_string_create_str(B, "Axe");
uint16_t weapon_two_damage = 5;
ns(Weapon_ref_t) sword = ns(Weapon_create(B, weapon_one_name, weapon_one_damage));
ns(Weapon_ref_t) axe = ns(Weapon_create(B, weapon_two_name, weapon_two_damage));
final int weaponTwoName = builder.writeString("Axe"); final int weaponTwoDamage = 5;
final swordBuilder = new myGame.WeaponBuilder(builder) ..begin() ..addNameOffset(weaponOneName) ..addDamage(weaponOneDamage); final int sword = swordBuilder.finish();
final axeBuilder = new myGame.WeaponBuilder(builder) ..begin() ..addNameOffset(weaponTwoName) ..addDamage(weaponTwoDamage); final int axe = axeBuilder.finish();
// The generated ObjectBuilder classes offer an easier to use alternative // at the cost of requiring some additional reference allocations. If memory // usage is critical, or if you'll be working with especially large messages // or tables, you should prefer using the generated Builder classes. // The following code would produce an identical buffer as above. final String weaponOneName = "Sword"; final int weaponOneDamage = 3;
final String weaponTwoName = "Axe"; final int weaponTwoDamage = 5;
final myGame.WeaponBuilder sword = new myGame.WeaponObjectBuilder( name: weaponOneName, damage: weaponOneDamage, );
final myGame.WeaponBuilder axe = new myGame.WeaponObjectBuilder( name: weaponTwoName, damage: weaponTwoDamage, );
</div>
<div class="language-lua">
~~~{.lua}
local weaponOne = builder:CreateString("Sword")
local weaponTwo = builder:CreateString("Axe")
-- Create the first 'Weapon'
weapon.Start(builder)
weapon.AddName(builder, weaponOne)
weapon.AddDamage(builder, 3)
local sword = weapon.End(builder)
-- Create the second 'Weapon'
weapon.Start(builder)
weapon.AddName(builder, weaponTwo)
weapon.AddDamage(builder, 5)
local axe = weapon.End(builder)
let weapon_offsets = map(weapon_names) name, i: let ns = builder.CreateString(name) MyGame_Sample_WeaponBuilder { b } .start() .add_name(ns) .add_damage(weapon_damages[i]) .end()
</div>
<div class="language-rust">
~~~{.rs}
// Serialize some weapons for the Monster: A 'sword' and an 'axe'.
let weapon_one_name = builder.create_string("Sword");
let weapon_two_name = builder.create_string("Axe");
// Use the `Weapon::create` shortcut to create Weapons with named field
// arguments.
let sword = Weapon::create(&mut builder, &WeaponArgs{
name: Some(weapon_one_name),
damage: 3,
});
let axe = Weapon::create(&mut builder, &WeaponArgs{
name: Some(weapon_two_name),
damage: 5,
});
// start creating the weapon by calling startWeapon let weapon1Start = Weapon.startWeapon(&builder) Weapon.add(name: weapon1Name, &builder) Weapon.add(damage: 3, &builder) // end the object by passing the start point for the weapon 1 let sword = Weapon.endWeapon(&builder, start: weapon1Start)
let weapon2Start = Weapon.startWeapon(&builder) Weapon.add(name: weapon2Name, &builder) Weapon.add(damage: 5, &builder) let axe = Weapon.endWeapon(&builder, start: weapon2Start)
</div>
Now let's create our monster, the `orc`. For this `orc`, lets make him
`red` with rage, positioned at `(1.0, 2.0, 3.0)`, and give him
a large pool of hit points with `300`. We can give him a vector of weapons
to choose from (our `Sword` and `Axe` from earlier). In this case, we will
equip him with the `Axe`, since it is the most powerful of the two. Lastly,
let's fill his inventory with some potential treasures that can be taken once he
is defeated.
Before we serialize a monster, we need to first serialize any objects that are
contained therein, i.e. we serialize the data tree using depth-first, pre-order
traversal. This is generally easy to do on any tree structures.
<div class="language-cpp">
~~~{.cpp}
// Serialize a name for our monster, called "Orc".
auto name = builder.CreateString("Orc");
// Create a `vector` representing the inventory of the Orc. Each number
// could correspond to an item that can be claimed after he is slain.
unsigned char treasure[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto inventory = builder.CreateVector(treasure, 10);
// Create a vector
representing the inventory of the
Orc. Each number // could correspond to an item that can be claimed
after he is slain. byte[] treasure = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int
inv = Monster.createInventoryVector(builder, treasure);
</div>
<div class="language-kotlin">
~~~{.kt}
// Serialize a name for our monster, called "Orc".
val name = builder.createString("Orc")
// Create a `vector` representing the inventory of the Orc. Each number
// could correspond to an item that can be claimed after he is slain.
val treasure = byteArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
val inv = Monster.createInventoryVector(builder, treasure)
// Create a vector
representing the inventory of the
Orc. Each number // could correspond to an item that can be claimed
after he is slain. // Note: Since we prepend the bytes, this loop
iterates in reverse order. Monster.StartInventoryVector(builder, 10);
for (int i = 9; i >= 0; i--) { builder.AddByte((byte)i); } var inv =
builder.EndVector();
</div>
<div class="language-go">
~~~{.go}
// Serialize a name for our monster, called "Orc".
name := builder.CreateString("Orc")
// Create a `vector` representing the inventory of the Orc. Each number
// could correspond to an item that can be claimed after he is slain.
// Note: Since we prepend the bytes, this loop iterates in reverse.
sample.MonsterStartInventoryVector(builder, 10)
for i := 9; i >= 0; i-- {
builder.PrependByte(byte(i))
}
inv := builder.EndVector(10)
vector
representing the inventory of the Orc. Each
numberMyGame.Sample.Monster.StartInventoryVector(builder, 10) for i in reversed(range(0, 10)): builder.PrependByte(i) inv = builder.EndVector()
</div>
<div class="language-javascript">
~~~{.js}
// Serialize a name for our monster, called 'Orc'.
var name = builder.createString('Orc');
// Create a `vector` representing the inventory of the Orc. Each number
// could correspond to an item that can be claimed after he is slain.
var treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var inv = MyGame.Sample.Monster.createInventoryVector(builder, treasure);
// Create a vector
representing the inventory of the
Orc. Each number // could correspond to an item that can be claimed
after he is slain. let treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; let
inv = MyGame.Sample.Monster.createInventoryVector(builder,
treasure);
</div>
<div class="language-php">
~~~{.php}
// Serialize a name for our monster, called "Orc".
$name = $builder->createString("Orc");
// Create a `vector` representing the inventory of the Orc. Each number
// could correspond to an item that can be claimed after he is slain.
$treasure = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
$inv = \MyGame\Sample\Monster::CreateInventoryVector($builder, $treasure);
// Create a vector
representing the inventory of the
Orc. Each number // could correspond to an item that can be claimed
after he is slain. uint8_t treasure[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
flatbuffers_uint8_vec_ref_t inventory; // c_vec_len
is the
convenience macro we defined earlier. inventory =
flatbuffers_uint8_vec_create(B, treasure, c_vec_len(treasure));
</div>
<div class="language-dart">
~~~{.dart}
// Serialize a name for our monster, called "Orc".
final int name = builder.writeString('Orc');
// Create a list representing the inventory of the Orc. Each number
// could correspond to an item that can be claimed after he is slain.
final List<int> treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
final inventory = builder.writeListUint8(treasure);
// The following code should be used instead if you intend to use the
// ObjectBuilder classes:
// Serialize a name for our monster, called "Orc".
final String name = 'Orc';
// Create a list representing the inventory of the Orc. Each number
// could correspond to an item that can be claimed after he is slain.
final List<int> treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
-- Create a `vector` representing the inventory of the Orc. Each number
-- could correspond to an item that can be claimed after he is slain.
-- Note: Since we prepend the bytes, this loop iterates in reverse.
monster.StartInventoryVector(builder, 10)
for i=10,1,-1 do
builder:PrependByte(i)
end
local inv = builder:EndVector(10)
</div>
<div class="language-lobster">
~~~{.lobster}
// Name of the monster.
let name = builder.CreateString("Orc")
// Inventory.
let inv = builder.MyGame_Sample_MonsterCreateInventoryVector(map(10): _)
// Inventory. let inventory = builder.create_vector(&[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
</div>
<div class="language-swift">
~~~{.swift}
// Name of the Monster.
let name = builder.create(string: "Orc")
// create inventory
let inventory: [Byte] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let inventoryOffset = builder.createVector(inventory)
We serialized two built-in data types (string
and
vector
) and captured their return values. These values are
offsets into the serialized data, indicating where they are stored, such
that we can refer to them below when adding fields to our monster.
Note: To create a vector
of nested objects (e.g.
table
s, string
s, or other
vector
s), collect their offsets into a temporary data
structure, and then create an additional vector
containing
their offsets.
If instead of creating a vector from an existing array you serialize elements individually one by one, take care to note that this happens in reverse order, as buffers are built back to front.
For example, take a look at the two Weapon
s that we
created earlier (Sword
and Axe
). These are
both FlatBuffer table
s, whose offsets we now store in
memory. Therefore we can create a FlatBuffer vector
to
contain these offsets.
// Pass the weaps
array into the
createWeaponsVector()
method to create a FlatBuffer vector.
int weapons = Monster.createWeaponsVector(builder, weaps);
</div>
<div class="language-kotlin">
~~~{.kt}
// Place the two weapons into an array, and pass it to the `createWeaponsVector()` method to
// create a FlatBuffer vector.
val weaps = intArrayOf(sword, axe)
// Pass the `weaps` array into the `createWeaponsVector()` method to create a FlatBuffer vector.
val weapons = Monster.createWeaponsVector(builder, weaps)
// Pass the weaps
array into the
CreateWeaponsVector()
method to create a FlatBuffer vector.
var weapons = Monster.CreateWeaponsVector(builder, weaps);
</div>
<div class="language-go">
~~~{.go}
// Create a FlatBuffer vector and prepend the weapons.
// Note: Since we prepend the data, prepend them in reverse order.
sample.MonsterStartWeaponsVector(builder, 2)
builder.PrependUOffsetT(axe)
builder.PrependUOffsetT(sword)
weapons := builder.EndVector(2)
// If using the ObjectBuilders, just create an array from the two
Weapon
s final List<myGame.WeaponBuilder> weaps =
[sword, axe];
</div>
<div class="language-lua">
~~~{.lua}
-- Create a FlatBuffer vector and prepend the weapons.
-- Note: Since we prepend the data, prepend them in reverse order.
monster.StartWeaponsVector(builder, 2)
builder:PrependUOffsetTRelative(axe)
builder:PrependUOffsetTRelative(sword)
local weapons = builder:EndVector(2)
Note that vectors of structs are serialized differently from tables,
since structs are stored in-line in the vector. For example, to create a
vector for the path
field above:
// Otherwise, using the ObjectBuilder classes: // The dart
implementation provides a simple interface for writing vectors // of
structs, in writeListOfStructs
. This method takes //
List<ObjectBuilder>
and is used by the generated
builder classes. final List<myGame.Vec3ObjectBuilder> path = [ new
myGame.Vec3ObjectBuilder(x: 1.0, y: 2.0, z: 3.0), new
myGame.Vec3ObjectBuilder(x: 4.0, y: 5.0, z: 6.0) ];
</div>
<div class="language-lua">
~~~{.lua}
-- Create a FlatBuffer vector and prepend the path locations.
-- Note: Since we prepend the data, prepend them in reverse order.
monster.StartPathVector(builder, 2)
vec3.CreateVec3(builder, 1.0, 2.0, 3.0)
vec3.CreateVec3(builder, 4.0, 5.0, 6.0)
local path = builder:EndVector(2)
// Note that, for convenience, it is also valid to create a vector of // references to structs, like this: // let path = builder.create_vector(&[&x, &y]);
</div>
<div class="language-swift">
~~~{.swift}
//
let points = fbb.createVector(ofStructs: [
Vec3(x: 1, y: 2, z: 3),
Vec3(x: 4, y: 5, z: 6)
])
// OR
var vec3 = [
Vec3(x: 1, y: 2, z: 3),
Vec3(x: 4, y: 5, z: 6)
]
Monster.startVectorOfVec3(2, in: &fbb)
for i in obj {
_ = create(struct: i)
}
let points = fbb.endVector(len: size)
We have now serialized the non-scalar components of the orc, so we can serialize the monster itself:
// Set his hit points to 300 and his mana to 150. int hp = 300; int mana = 150;
// Finally, create the monster using the CreateMonster
helper function // to set all fields. auto orc = CreateMonster(builder,
&position, mana, hp, name, inventory, Color_Red, weapons,
Equipment_Weapon, axe.Union(), path);
</div>
<div class="language-java">
~~~{.java}
// Create our monster using `startMonster()` and `endMonster()`.
Monster.startMonster(builder);
Monster.addPos(builder, Vec3.createVec3(builder, 1.0f, 2.0f, 3.0f));
Monster.addName(builder, name);
Monster.addColor(builder, Color.Red);
Monster.addHp(builder, (short)300);
Monster.addInventory(builder, inv);
Monster.addWeapons(builder, weapons);
Monster.addEquippedType(builder, Equipment.Weapon);
Monster.addEquipped(builder, axe);
Monster.addPath(builder, path);
int orc = Monster.endMonster(builder);
// Define an equipment union. create
calls in C has a
single // argument for unions where C++ has both a type and a data
argument. ns(Equipment_union_ref_t) equipped =
ns(Equipment_as_Weapon(axe)); ns(Vec3_t) pos = { 1.0f, 2.0f, 3.0f };
ns(Monster_create_as_root(B, &pos, mana, hp, name, inventory,
ns(Color_Red), weapons, equipped, path));
</div>
<div class="language-dart">
~~~{.dart}
// Using the Builder API:
// Set his hit points to 300 and his mana to 150.
final int hp = 300;
final int mana = 150;
final monster = new myGame.MonsterBuilder(builder)
..begin()
..addNameOffset(name)
..addInventoryOffset(inventory)
..addWeaponsOffset(weapons)
..addEquippedType(myGame.EquipmentTypeId.Weapon)
..addEquippedOffset(axe)
..addHp(hp)
..addMana(mana)
..addPos(vec3Builder.finish(1.0, 2.0, 3.0))
..addPathOffset(path)
..addColor(myGame.Color.Red);
final int orc = monster.finish();
// -Or- using the ObjectBuilder API:
// Set his hit points to 300 and his mana to 150.
final int hp = 300;
final int mana = 150;
// Note that these parameters are optional - it is not necessary to set
// all of them.
// Also note that it is not necessary to `finish` the builder helpers above
// - the generated code will automatically reuse offsets if the same object
// is used in more than one place (e.g. the axe appearing in `weapons` and
// `equipped`).
final myGame.MonsterBuilder orcBuilder = new myGame.MonsterBuilder(
name: name,
inventory: treasure,
weapons: weaps,
equippedType: myGame.EquipmentTypeId.Weapon,
equipped: axe,
path: path,
hp: hp,
mana: mana,
pos: new myGame.Vec3Builder(x: 1.0, y: 2.0, z: 3.0),
color: myGame.Color.Red,
path: [
new myGame.Vec3ObjectBuilder(x: 1.0, y: 2.0, z: 3.0),
new myGame.Vec3ObjectBuilder(x: 4.0, y: 5.0, z: 6.0)
]);
final int orc = orcBuilder.finish(builder);
Note how we create Vec3
struct in-line in the table.
Unlike tables, structs are simple combinations of scalars that are
always stored inline, just like scalars themselves.
Important: Unlike structs, you should not nest
tables or other objects, which is why we created all the
strings/vectors/tables that this monster refers to before
start
. If you try to create any of them between
start
and end
, you will get an
assert/exception/panic depending on your language.
Note: Since we are passing 150
as the
mana
field, which happens to be the default value, the
field will not actually be written to the buffer, since the default
value will be returned on query anyway. This is a nice space savings,
especially if default values are common in your data. It also means that
you do not need to be worried about adding a lot of fields that are only
used in a small number of instances, as it will not bloat the buffer if
unused.
ns(Monster_hp_add(B, hp)); // Notice that
Monser_name_add
adds a string reference unlike the //
add_str and add_strn variants. ns(Monster_name_add(B, name));
ns(Monster_inventory_add(B, inventory)); ns(Monster_color_add(B,
ns(Color_Red))); ns(Monster_weapons_add(B, weapons));
ns(Monster_equipped_add(B, equipped)); // Complete the monster object
and make it the buffer root object. ns(Monster_end_as_root(B));
</div>
<div class="language-swift">
~~~{.swift}
let start = Monster.startMonster(&builder)
Monster.add(pos: Vec3(x: 1, y: 2, z: 3), &builder)
Monster.add(hp: 300, &builder)
Monster.add(name: name, &builder)
Monster.addVectorOf(inventory: inventoryOffset, &builder)
Monster.add(color: .red, &builder)
Monster.addVectorOf(weapons: weaponsOffset, &builder)
Monster.add(equippedType: .weapon, &builder)
Monster.add(equipped: axe, &builder)
var orc = Monster.endMonster(&builder, start: start)
Before finishing the serialization, let's take a quick look at
FlatBuffer union Equipped
. There are two parts to each
FlatBuffer union
. The first is a hidden field
_type
that is generated to hold the type of
table
referred to by the union
. This allows
you to know which type to cast to at runtime. Second is the
union
's data.
In our example, the last two things we added to our
Monster
were the Equipped Type
and the
Equipped
union itself.
Here is a repetition of these lines, to help highlight them more clearly:
// in the ObjectBuilder API: equippedTypeId: myGame.EquipmentTypeId.Weapon, // Union type equipped: axe, // Union data
</div>
<div class="language-lua">
~~~{.lua}
monster.AddEquippedType(builder, equipment.Weapon) -- Union type
monster.AddEquipped(builder, axe) -- Union data
After you have created your buffer, you will have the offset to the
root of the data in the orc
variable, so you can finish the
buffer by calling the appropriate finish
method.
The buffer is now ready to be stored somewhere, sent over the network, be compressed, or whatever you'd like to do with it. You can access the buffer like so:
// Alternatively this copies the above data out of the ByteBuffer for you: byte[] buf = builder.sizedByteArray();
</div>
<div class="language-kotlin">
~~~{.kt}
// This must be called after `finish()`.
val buf = builder.dataBuffer()
// The data in this ByteBuffer does NOT start at 0, but at buf.position().
// The number of bytes is buf.remaining().
// Alternatively this copies the above data out of the ByteBuffer for you:
val buf = builder.sizedByteArray()
// Alternatively this copies the above data out of the ByteBuffer for you: byte[] buf = builder.SizedByteArray();
</div>
<div class="language-go">
~~~{.go}
// This must be called after `Finish()`.
buf := builder.FinishedBytes() // Of type `byte[]`.
// Allocate and extract a readable buffer from internal builder heap.
// The returned buffer must be deallocated using free
. //
NOTE: Finalizing the buffer does NOT change the builder, it // just
creates a snapshot of the builder content. buf =
flatcc_builder_finalize_buffer(B, &size); // use buf free(buf);
// Optionally reset builder to reuse builder without deallocating // internal stack and heap. flatcc_builder_reset(B); // build next buffer. // ...
// Cleanup. flatcc_builder_clear(B);
</div>
<div class="language-dart">
~~~{.dart}
final Uint8List buf = builder.finish(orc);
Now you can write the bytes to a file or send them over the network. Make sure your file mode (or transfer protocol) is set to BINARY, not text. If you transfer a FlatBuffer in text mode, the buffer will be corrupted, which will lead to hard to find problems when you read the buffer.
Now that we have successfully created an Orc
FlatBuffer,
the monster data can be saved, sent over a network, etc. Let's now
adventure into the inverse, and access a FlatBuffer.
This section requires the same import/include, namespace, etc. requirements as before:
using namespace MyGame::Sample; // Specified in the schema.
</div>
<div class="language-java">
~~~{.java}
import MyGame.Sample.*; //The `flatc` generated files. (Monster, Vec3, etc.)
import com.google.flatbuffers.FlatBufferBuilder;
import com.google.flatbuffers.FlatBufferBuilder
</div>
<div class="language-csharp">
~~~{.cs}
using Google.FlatBuffers;
using MyGame.Sample; // The `flatc` generated files. (Monster, Vec3, etc.)
flatc
.import MyGame.Sample.Any import MyGame.Sample.Color import MyGame.Sample.Monster import MyGame.Sample.Vec3
</div>
<div class="language-javascript">
~~~{.js}
// The following code is an example - use your desired module flavor by transpiling from TS.
var flatbuffers = require('/js/flatbuffers').flatbuffers;
var MyGame = require('./monster_generated').MyGame; // Generated by `flatc`.
//--------------------------------------------------------------------------//
// The following code an example for browser-based HTML/JavaScript. Use the above code
// for JavaScript module loaders (e.g. Node.js).
<script src="../js/flatbuffers.js"></script>
<script src="monster_generated.js"></script> // Generated by `flatc`.
// note: the ./monster_generated.js
file was previously
generated by flatc
above using the monster.fbs
schema import { MyGame } from './monster_generated';
</div>
<div class="language-php">
~~~{.php}
// It is recommended that your use PSR autoload when using FlatBuffers in PHP.
// Here is an example from `SampleBinary.php`:
function __autoload($class_name) {
// The last segment of the class name matches the file name.
$class = substr($class_name, strrpos($class_name, "\\") + 1);
$root_dir = join(DIRECTORY_SEPARATOR, array(dirname(dirname(__FILE__)))); // `flatbuffers` root.
// Contains the `*.php` files for the FlatBuffers library and the `flatc` generated files.
$paths = array(join(DIRECTORY_SEPARATOR, array($root_dir, "php")),
join(DIRECTORY_SEPARATOR, array($root_dir, "samples", "MyGame", "Sample")));
foreach ($paths as $path) {
$file = join(DIRECTORY_SEPARATOR, array($path, $class . ".php"));
if (file_exists($file)) {
require($file);
break;
}
}
}
#undef ns #define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) // Specified in the schema.
</div>
<div class="language-dart">
~~~{.dart}
import 'package:flat_buffers/flat_buffers.dart' as fb;
import './monster_my_game.sample_generated.dart' as myGame;
-- require the generated files from flatc
. local color =
require("MyGame.Sample.Color") local equipment =
require("MyGame.Sample.Equipment") local monster =
require("MyGame.Sample.Monster") local vec3 =
require("MyGame.Sample.Vec3") local weapon =
require("MyGame.Sample.Weapon")
</div>
<div class="language-lobster">
~~~{.lobster}
import from "../lobster/" // Where to find flatbuffers.lobster
import monster_generated
// import the generated code #[allow(dead_code, unused_imports)] #[path = "./monster_generated.rs"] mod monster_generated; pub use monster_generated::my_game::sample::{get_root_as_monster, Color, Equipment, Monster, MonsterArgs, Vec3, Weapon, WeaponArgs};
</div>
Then, assuming you have a buffer of bytes received from disk,
network, etc., you can start accessing the buffer like so:
**Again, make sure you read the bytes in BINARY mode, otherwise the code below
won't work.**
<div class="language-cpp">
~~~{.cpp}
uint8_t *buffer_pointer = /* the data you just read */;
// Get a pointer to the root object inside the buffer.
auto monster = GetMonster(buffer_pointer);
// `monster` is of type `Monster *`.
// Note: root object pointers are NOT the same as `buffer_pointer`.
// `GetMonster` is a convenience function that calls `GetRoot<Monster>`,
// the latter is also available for non-root types.
// Get an accessor to the root object inside the buffer. Monster monster = Monster.getRootAsMonster(buf);
</div>
<div class="language-kotlin">
~~~{.kt}
val bytes = /* the data you just read */
val buf = java.nio.ByteBuffer.wrap(bytes)
// Get an accessor to the root object inside the buffer.
Monster monster = Monster.getRootAsMonster(buf)
// Get an accessor to the root object inside the buffer. var monster = Monster.GetRootAsMonster(buf);
</div>
<div class="language-go">
~~~{.go}
var buf []byte = /* the data you just read */
// Get an accessor to the root object inside the buffer.
monster := sample.GetRootAsMonster(buf, 0)
// Note: We use `0` for the offset here, which is typical for most buffers
// you would read. If you wanted to read from `builder.Bytes` directly, you
// would need to pass in the offset of `builder.Head()`, as the builder
// constructs the buffer backwards, so may not start at offset 0.
// Get an accessor to the root object inside the buffer. monster = MyGame.Sample.Monster.Monster.GetRootAs(buf, 0)
0
for the offset here, which is typical for most
buffersbuilder.Bytes
directly,builder.Head()
, as the
builder</div>
<div class="language-javascript">
~~~{.js}
// the data you just read, as a `Uint8Array`
// Note that the example here uses `readFileSync` from the built-in `fs` module,
// but other methods for accessing the file contents will also work.
var bytes = new Uint8Array(readFileSync('./monsterdata.bin'));
var buf = new flatbuffers.ByteBuffer(bytes);
// Get an accessor to the root object inside the buffer.
var monster = MyGame.Sample.Monster.getRootAsMonster(buf);
let buf = new flatbuffers.ByteBuffer(bytes);
// Get an accessor to the root object inside the buffer. let monster = MyGame.Sample.Monster.getRootAsMonster(buf);
</div>
<div class="language-php">
~~~{.php}
$bytes = /* the data you just read, in a string */
$buf = Google\FlatBuffers\ByteBuffer::wrap($bytes);
// Get an accessor to the root object inside the buffer.
$monster = \MyGame\Sample\Monster::GetRootAsMonster($buf);
// Note: root object pointers are NOT the same as the
buffer
pointer.
</div>
<div class="language-dart">
~~~{.dart}
List<int> data = ... // the data, e.g. from file or network
// A generated factory constructor that will read the data.
myGame.Monster monster = new myGame.Monster(data);
-- Convert the string representation into binary array Lua structure
local buf = flatbuffers.binaryArray.New(bufAsString)
-- Get an accessor to the root object insert the buffer
local mon = monster.GetRootAsMonster(buf, 0)
</div>
<div class="language-lobster">
~~~{.lobster}
buf = /* the data you just read, in a string */
// Get an accessor to the root object inside the buffer.
let monster = MyGame_Sample_GetRootAsMonster(buf)
// Get an accessor to the root object inside the buffer. let monster = get_root_as_monster(buf);
</div>
<div class="language-swift">
~~~{.swift}
// create a ByteBuffer(:) from an [UInt8] or Data()
var buf = // Get your data
// Get an accessor to the root object inside the buffer.
let monster: Monster = try! getCheckedRoot(byteBuffer: &byteBuffer)
// let monster: Monster = getRoot(byteBuffer: &byteBuffer)
If you look in the generated files from the schema compiler, you will
see it generated accessors for all non-deprecated
fields.
For example:
These should hold 300
, 150
, and
"Orc"
respectively.
Note: The default value 150
wasn't stored in
mana
, but we are still able to retrieve it.
To access sub-objects, in the case of our pos
, which is
a Vec3
:
// Note: Whenever you access a new object, like in
Pos()
, a new temporary // accessor object gets created. If
your code is very performance sensitive, // you can pass in a pointer to
an existing Vec3
instead of nil
. This //
allows you to reuse it across many calls to reduce the amount of object
// allocation/garbage collection.
</div>
<div class="language-python">
~~~{.py}
pos = monster.Pos()
x = pos.X()
y = pos.Y()
z = pos.Z()
x
, y
, and z
will contain
1.0
, 2.0
, and 3.0
,
respectively.
Note: Had we not set pos
during serialization, it
would be a null
-value.
Similarly, we can access elements of the inventory
vector
by indexing it. You can also iterate over the length
of the array/vector representing the FlatBuffers
vector
.
// Note that this vector is returned as a slice, because direct
access for // this type, a u8
vector, is safe on all
platforms: let third_item = inv[2];
</div>
<div class="language-swift">
~~~{.swift}
// Get a the count of objects in the vector
let count = monster.inventoryCount
// get item at index 4
let object = monster.inventory(at: 4)
// or you can fetch the entire array
let inv = monster.inventory
// inv[4] should equal object
For vector
s of table
s, you can access the
elements like any other vector, except you need to handle the result as
a FlatBuffer table
:
let wep2 = weps.get(1); let second_weapon_name = wep2.name(); let second_weapon_damage = wep2.damage();
</div>
<div class="language-swift">
~~~{.swift}
// Get the count of weapon objects
let wepsCount = monster.weaponsCount
let weapon2 = monster.weapons(at: 1)
let weaponName = weapon2.name
let weaponDmg = weapon2.damage
Last, we can access our Equipped
FlatBuffer
union
. Just like when we created the union
, we
need to get both parts of the union
: the type and the
data.
We can access the type to dynamically cast the data as needed (since
the union
only stores a FlatBuffer table
).
if (union_type == Equipment_Weapon) { auto weapon =
static_cast<const Weapon*>(monster->equipped()); // Requires
static_cast
// to type const Weapon*
.
auto weapon_name = weapon->name()->str(); // "Axe"
auto weapon_damage = weapon->damage(); // 5
}
</div>
<div class="language-java">
~~~{.java}
int unionType = monster.EquippedType();
if (unionType == Equipment.Weapon) {
Weapon weapon = (Weapon)monster.equipped(new Weapon()); // Requires explicit cast
// to `Weapon`.
String weaponName = weapon.name(); // "Axe"
short weaponDamage = weapon.damage(); // 5
}
if (unionType == Equipment.Weapon) { val weapon =
monster.equipped(Weapon()) as Weapon // Requires explicit cast // to
Weapon
.
val weaponName = weapon.name // "Axe"
val weaponDamage = weapon.damage // 5
}
</div>
<div class="language-csharp">
~~~{.cs}
var unionType = monster.EquippedType;
if (unionType == Equipment.Weapon) {
var weapon = monster.Equipped<Weapon>().Value;
var weaponName = weapon.Name; // "Axe"
var weaponDamage = weapon.Damage; // 5
}
if monster.Equipped(unionTable) { unionType := monster.EquippedType()
if unionType == sample.EquipmentWeapon {
// Create a `sample.Weapon` object that can be initialized with the contents
// of the `flatbuffers.Table` (`unionTable`), which was populated by
// `monster.Equipped()`.
unionWeapon = new(sample.Weapon)
unionWeapon.Init(unionTable.Bytes, unionTable.Pos)
weaponName = unionWeapon.Name()
weaponDamage = unionWeapon.Damage()
}
}
</div>
<div class="language-python">
~~~{.py}
union_type = monster.EquippedType()
if union_type == MyGame.Sample.Equipment.Equipment().Weapon:
# `monster.Equipped()` returns a `flatbuffers.Table`, which can be used to
# initialize a `MyGame.Sample.Weapon.Weapon()`.
union_weapon = MyGame.Sample.Weapon.Weapon()
union_weapon.Init(monster.Equipped().Bytes, monster.Equipped().Pos)
weapon_name = union_weapon.Name() // 'Axe'
weapon_damage = union_weapon.Damage() // 5
if (unionType == MyGame.Sample.Equipment.Weapon) { var weaponName = monster.equipped(new MyGame.Sample.Weapon()).name(); // 'Axe' var weaponDamage = monster.equipped(new MyGame.Sample.Weapon()).damage(); // 5 }
</div>
<div class="language-typescript">
~~~{.ts}
let unionType = monster.equippedType();
if (unionType == MyGame.Sample.Equipment.Weapon) {
let weaponName = monster.equipped(new MyGame.Sample.Weapon()).name(); // 'Axe'
let weaponDamage = monster.equipped(new MyGame.Sample.Weapon()).damage(); // 5
}
if ($union_type == \MyGame\Sample\Equipment::Weapon) { $weapon_name = $monster->getEquipped(new \MyGame\Sample\Weapon())->getName(); // "Axe" $weapon_damage = $monster->getEquipped(new \MyGame\Sample\Weapon())->getDamage(); // 5 }
</div>
<div class="language-c">
~~~{.c}
// Access union type field.
if (ns(Monster_equipped_type(monster)) == ns(Equipment_Weapon)) {
// Cast to appropriate type:
// C allows for silent void pointer assignment, so we need no explicit cast.
ns(Weapon_table_t) weapon = ns(Monster_equipped(monster));
const char *weapon_name = ns(Weapon_name(weapon)); // "Axe"
uint16_t weapon_damage = ns(Weapon_damage(weapon)); // 5
}
if (unionType == myGame.EquipmentTypeId.Weapon.value) { myGame.Weapon weapon = mon.equipped as myGame.Weapon;
var weaponName = weapon.name; // "Axe"
var weaponDamage = weapon.damage; // 5
}
</div>
<div class="language-lua">
~~~{.lua}
local unionType = mon:EquippedType()
if unionType == equipment.Weapon then
local unionWeapon = weapon.New()
unionWeapon:Init(mon:Equipped().bytes, mon:Equipped().pos)
local weaponName = unionWeapon:Name() -- 'Axe'
local weaponDamage = unionWeapon:Damage() -- 5
end
if union_type == MyGame_Sample_Equipment_Weapon: //
monster.equipped_as_Weapon
returns a FlatBuffer handle much
like normal table fields, // but this is only valid to call if we
already know it is the correct type. let union_weapon =
monster.equipped_as_Weapon
let weapon_name = union_weapon.name // "Axe"
let weapon_damage = union_weapon.damage // 5
</div>
<div class="language-rust">
~~~{.rs}
// Get and test the `Equipment` union (`equipped` field).
// `equipped_as_weapon` returns a FlatBuffer handle much like normal table
// fields, but this will return `None` if the union is not actually of that
// type.
if monster.equipped_type() == Equipment::Weapon {
let equipped = monster.equipped_as_weapon().unwrap();
let weapon_name = equipped.name();
let weapon_damage = equipped.damage();
As you saw above, typically once you have created a FlatBuffer, it is read-only from that moment on. There are, however, cases where you have just received a FlatBuffer, and you'd like to modify something about it before sending it on to another recipient. With the above functionality, you'd have to generate an entirely new FlatBuffer, while tracking what you modified in your own data structures. This is inconvenient.
For this reason FlatBuffers can also be mutated in-place. While this is great for making small fixes to an existing buffer, you generally want to create buffers from scratch whenever possible, since it is much more efficient and the API is much more general purpose.
To get non-const accessors, invoke flatc
with
--gen-mutable
.
Similar to how we read fields using the accessors above, we can now use the mutators like so:
We use the somewhat verbose term mutate
instead of
set
to indicate that this is a special use case, not to be
confused with the default way of constructing FlatBuffer data.
After the above mutations, you can send on the FlatBuffer to a new recipient without any further work!
Note that any mutate
functions on a table will return a
boolean, which is false
if the field we're trying to set is
not present in the buffer. Fields are not present if they weren't set,
or even if they happen to be equal to the default value. For example, in
the creation code above, the mana
field is equal to
150
, which is the default value, so it was never stored in
the buffer. Trying to call the corresponding mutate
method
for mana
on such data will return false
, and
the value won't actually be modified!
One way to solve this is to call ForceDefaults
on a
FlatBufferBuilder to force all fields you set to actually be written.
This, of course, increases the size of the buffer somewhat, but this may
be acceptable for a mutable buffer.
If this is not sufficient, other ways of mutating FlatBuffers may be
supported in your language through an object based API
(--gen-object-api
) or reflection. See the individual
language documents for support.
flatc
as a JSON Conversion ToolIf you are working with C, C++, or Lobster, you can parse JSON at
runtime. If your language does not support JSON at the moment,
flatc
may provide an alternative. Using flatc
is often the preferred method, as it doesn't require you to add any new
code to your program. It is also efficient, since you can ship with the
binary data. The drawback is that it requires an extra step for your
users/developers to perform (although it may be able to be automated as
part of your compilation).
Let's say you have a JSON file that describes your monster. In this
example, we will use the file
flatbuffers/samples/monsterdata.json
.
Here are the contents of the file:
{
"pos": {
"x": 1.0,
"y": 2.0,
"z": 3.0
},
"hp": 300,
"name": "Orc",
"weapons": [
{
"name": "axe",
"damage": 100
},
{
"name": "bow",
"damage": 90
}
],
"equipped_type": "Weapon",
"equipped": {
"name": "bow",
"damage": 90
}
}
You can run this file through the flatc
compiler with
the -b
flag and our monster.fbs
schema to
produce a FlatBuffer binary file.
./../flatc --binary monster.fbs monsterdata.json
The output of this will be a file monsterdata.bin
, which
will contain the FlatBuffer binary representation of the contents from
our .json
file.
Converting from a FlatBuffer binary representation to JSON is supported as well:
./../flatc --json --raw-binary monster.fbs -- monsterdata.bin
This will convert monsterdata.bin
back to its original
JSON representation. You need to pass the corresponding FlatBuffers
schema so that flatc knows how to interpret the binary buffer. Since
monster.fbs
does not specify an explicit
file_identifier
for binary buffers, flatc
needs to be forced into reading the .bin
file using the
--raw-binary
option.
The FlatBuffer binary representation does not explicitly encode
default values, therefore they are not present in the resulting JSON
unless you specify --defaults-json
.
If you intend to process the JSON with other tools, you may consider
switching on --strict-json
so that identifiers are quoted
properly.
Note: The resulting JSON file is not necessarily identical with the original JSON. If the binary representation contains floating point numbers, floats and doubles are rounded to 6 and 12 digits, respectively, in order to represent them as decimals in the JSON document.
Some languages support storing unions directly in a vector.
// File found in tests/union_vector/union_vector.fbs
namespace Example.VectorOfUnions;
// Demonstrates the ability to have vectors of unions, and also to
// store structs and strings in unions.
table Attacker {
sword_attack_damage: int;
}
struct Rapunzel {
hair_length: int;
}
struct BookReader {
books_read: int;
}
union Character {
MuLan: Attacker, // Can have name be different from type.
Rapunzel, // Or just both the same, as before.
Belle: BookReader,
BookFan: BookReader,
Other: string,
Unused: string
}
table Movie {
main_character: Character;
characters: [Character];
}
Analagously to how a union adds two fields to a table a vector of unions creates two different vectors: one for the union data and one for the data types.
var fbb = new FlatBufferBuilder(100);
var characterTypes = new[] { Character.MuLan, Character.Belle, Character.Other, }; var characterTypesOffset = Movie.CreateCharactersTypeVector(fbb, characterTypes);
var characters = new[] { Attacker.CreateAttacker(fbb, 10).Value, BookReader.CreateBookReader(fbb, 20).Value, fbb.CreateSharedString("Chip").Value, }; var charactersOffset = Movie.CreateCharactersVector(fbb, characters);
var movieOffset = Movie.CreateMovie( fbb, Character.Rapunzel, rapunzel, characterTypesOffset, charactersOffset); Movie.FinishMovieBuffer(fbb, movieOffset);
</div>
<div class="language-kotlin">
Kotlin supports vectors of unions, but it isn't currently documented.
</div>
<div class="language-swift">
Swift supports vectors of unions, but it isn't currently documented.
</div>
#### Reading
<div class="language-csharp">
~~~{.cs}
var movie = Movie.GetRootAsMovie(fbb.DataBuffer);
for (var i = 0; i <= movie.CharactersLength; i++)
{
if (movie.CharactersType(i) == Character.MuLan)
{
var mulanSwordDamage = movie.Characters<Attacker>(i).Value.SwordAttackDamage;
}
else if (movie.CharactersType(i) == Character.Belle)
{
var belleBooksRead = movie.Characters<BookReader>(i).Value.BooksRead;
}
else if (movie.CharactersType(i) == Character.Other)
{
var otherStr = movie.CharactersAsString(i);
}
}
Each language has a dedicated Use in XXX
page in the
Programmer's Guide to cover the nuances of FlatBuffers in that
language.
For your chosen language, see: