Overview
This tutorial provides a basic example of how to work with FlatBuffers. We will step through a simple example application, which shows you how to:
- Write a FlatBuffer
schema
file.
- Use the
flatc
FlatBuffer compiler.
- Parse JSON files that conform to a schema into FlatBuffer binary files.
- Use the generated files in many of the supported languages (such as C++, Java, and more.)
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:
Where to Find the Example Code
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:
Writing the Monsters' FlatBuffer Schema
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:
namespace MyGame.Sample;
enum Color:byte { Red = 0, Green, Blue = 2 }
union Equipment { Weapon }
struct Vec3 {
x:float;
y:float;
z:float;
}
table Monster {
pos:Vec3;
mana:short = 150;
hp:short = 100;
name:string;
friendly:bool = false (deprecated);
inventory:[ubyte];
color:Color = Blue;
weapons:[Weapon];
equipped:Equipment;
}
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
. All unspecified fields will default to 0
or NULL
. Another thing to note is the line friendly:bool = false (deprecated);
. Since you cannot delete fields from a table
(to support backwards compatability), 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
enum. 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
enum.
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.
More Information About Schemas
You can find a complete guide to writing schema
files in the Writing a schema section of the Programmer's Guide. You can also view the formal Grammar of the schema language.
Compiling the Monsters' Schema
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 to build flatc
, the FlatBuffer compiler.
Once flatc
is built successfully, compile the schema for your language:
cd flatbuffers/sample
./../flatc --cpp samples/monster.fbs
cd flatbuffers/sample
./../flatc --java samples/monster.fbs
cd flatbuffers/sample
./../flatc --csharp samples/monster.fbs
cd flatbuffers/sample
./../flatc --go samples/monster.fbs
cd flatbuffers/sample
./../flatc --python samples/monster.fbs
cd flatbuffers/sample
./../flatc --javascript samples/monster.fbs
cd flatbuffers/sample
./../flatc --php samples/monster.fbs
For a more complete guide to using the flatc
compiler, pleaes read the Using the schema compiler section of the Programmer's Guide.
Reading and Writing Monster FlatBuffers
Now that we have compiled the schema for our programming language, we can start creating some monsters and serializing/deserializing them from FlatBuffers.
Creating and Writing Orc FlatBuffers
The first step is to import/include the library, generated files, etc.
#include "monster_generate.h"
using namespace MyGame::Sample;
using FlatBuffers;
using MyGame.Sample;
import (
flatbuffers "github.com/google/flatbuffers/go"
sample "MyGame/Sample"
)
4 import MyGame.Sample.Color
5 import MyGame.Sample.Equipment
6 import MyGame.Sample.Monster
7 import MyGame.Sample.Vec3
8 import MyGame.Sample.Weapon
var flatbuffers = require('/js/flatbuffers').flatbuffers;
var MyGame = require('./monster_generated').MyGame;
<script src="../js/flatbuffers.js"></script>
<script src="monster_generated.js"></script>
function __autoload($class_name) {
$class = substr($class_name, strrpos($class_name, "\\") + 1);
$root_dir = join(DIRECTORY_SEPARATOR, array(dirname(dirname(__FILE__))));
$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;
}
}
}
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:
FlatBufferBuilder builder = new FlatBufferBuilder(0);
var builder = new FlatBufferBuilder(1);
builder := flatbuffers.NewBuilder(0)
3 builder = flatbuffers.Builder(0)
var builder = new flatbuffers.Builder(1);
After creating the builder
, we can start serializing our data. Before we make our orc
Monster, lets create some Weapon
s: a Sword
and an Axe
.
auto weapon_one_name = builder.CreateString("Sword");
short weapon_one_damage = 3;
auto weapon_two_name = builder.CreateString("Axe");
short weapon_two_damage = 5;
auto sword = CreateWeapon(builder, weapon_one_name, weapon_one_damage);
auto axe = CreateWeapon(builder, weapon_two_name, weapon_two_damage);
int weaponOneName = builder.createString("Sword")
short weaponOneDamage = 3;
int weaponTwoName = builder.createString("Axe");
short weaponTwoDamage = 5;
int sword = Weapon.createWeapon(builder, weaponOneName, weaponOneDamage);
int axe = Weapon.createWeapon(builder, weaponTwoName, weaponTwoDamage);
var weaponOneName = builder.CreateString("Sword");
var weaponOneDamage = 3;
var weaponTwoName = builder.CreateString("Axe");
var weaponTwoDamage = 5;
var sword = Weapon.CreateWeapon(builder, weaponOneName, (short)weaponOneDamage);
var axe = Weapon.CreateWeapon(builder, weaponTwoName, (short)weaponTwoDamage);
weaponOne := builder.CreateString("Sword")
weaponTwo := builder.CreateString("Axe")
sample.WeaponStart(builder)
sample.Weapon.AddName(builder, weaponOne)
sample.Weapon.AddDamage(builder, 3)
sword := sample.WeaponEnd(builder)
sample.WeaponStart(builder)
sample.Weapon.AddName(builder, weaponTwo)
sample.Weapon.AddDamage(builder, 5)
axe := sample.WeaponEnd(builder)
1 weapon_one = builder.CreateString(
'Sword')
2 weapon_two = builder.CreateString(
'Axe')
5 MyGame.Sample.Weapon.WeaponStart(builder)
6 MyGame.Sample.Weapon.WeaponAddName(builder, weapon_one)
7 MyGame.Sample.Weapon.WeaponAddDamage(builder, 3)
8 sword = MyGame.Sample.Weapon.WeaponEnd(builder)
11 MyGame.Sample.Weapon.WeaponStart(builder)
12 MyGame.Sample.Weapon.WeaponAddName(builder, weapon_two)
13 MyGame.Sample.Weapon.WeaponAddDamage(builder, 5)
14 axe = MyGame.Sample.Weapon.WeaponEnd(builder)
var weaponOne = builder.createString('Sword');
var weaponTwo = builder.createString('Axe');
MyGame.Sample.Weapon.startWeapon(builder);
MyGame.Sample.Weapon.addName(builder, weaponOne);
MyGame.Sample.Weapon.addDamage(builder, 3);
var sword = MyGame.Sample.Weapon.endWeapon(builder);
MyGame.Sample.Weapon.startWeapon(builder);
MyGame.Sample.Weapon.addName(builder, weaponTwo);
MyGame.Sample.Weapon.addDamage(builder, 5);
var axe = MyGame.Sample.Weapon.endWeapon(builder);
$weapon_one_name = $builder->createString("Sword");
$sword = \MyGame\Sample\Weapon::CreateWeapon($builder, $weapon_one_name, 3);
$weapon_two_name = $builder->createString("Axe");
$axe = \MyGame\Sample\Weapon::CreateWeapon($builder, $weapon_two_name, 5);
$weaps = array($sword, $axe);
$weapons = \MyGame\Sample\Monster::CreateWeaponsVector($builder, $weaps);
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 there-in, i.e. we serialize the data tree using depth-first, pre-order traversal. This is generally easy to do on any tree structures.
auto name = builder.CreateString("Orc");
unsigned char treasure = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto inventory = builder.CreateVector(treasure, 10);
int name = builder.createString("Orc");
byte[] treasure = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int inv = Monster.createInventoryVector(builder, treasure);
var name = builder.CreateString("Orc");
Monster.StartInventoryVector(builder, 10);
for (int i = 9; i >= 0; i--)
{
builder.AddByte((byte)i);
}
var inv = builder.EndVector();
name := builder.CreateString("Orc")
sample.MonsterStartInventoryVector(builder, 10)
for i := 9; i >= 0; i-- {
builder.PrependByte(byte(i))
}
int := builder.EndVector(10)
2 name = builder.CreateString(
"Orc")
7 MyGame.Sample.Monster.MonsterStartInventoryVector(builder, 10)
8 for i
in reversed(range(0, 10)):
10 inv = builder.EndVector(10)
var name = builder.createString('Orc');
var treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var inv = MyGame.Sample.Monster.createInventoryVector(builder, treasure);
$name = $builder->createString("Orc");
$treasure = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
$inv = \MyGame\Sample\Monster::CreateInventoryVector($builder, $treasure);
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.
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.
std::vector<flatbuffers::Offset<Weapon>> weapons_vector;
weapons_vector.push_back(sword);
weapons_vector.push_back(axe);
auto weapons = builder.CreateVector(weapons_vector);
int[] weaps = new int[2];
weaps[1] = sword;
weaps[2] = axe;
int weapons = Monster.createWeaponsVector(builder, weaps);
var weaps = new Offset<Weapon>[2];
weaps[0] = sword;
weaps[1] = axe;
var weapons = Monster.CreateWeaponsVector(builder, weaps);
sample.MonsterStartWeaponsVector(builder, 2)
builder.PrependUOffsetT(axe)
builder.PrependUOffsetT(sword)
weapons := builder.EndVector(2)
3 MyGame.Sample.Monster.MonsterStartWeaponsVector(builder, 2)
4 builder.PrependUOffsetTRelative(axe)
5 builder.PrependUOffsetTRelative(sword)
6 weapons = builder.EndVector(2)
var weaps = [sword, axe];
var weapons = MyGame.Sample.Monster.createWeaponsVector(builder, weaps);
$weaps = array($sword, $axe);
$weapons = \MyGame\Sample\Monster::CreateWeaponsVector($builder, $weaps);
To create a struct
, use the Vec3
class/struct that was generated by flatc
:
auto pos = Vec3(1.0f, 2.0f, 3.0f);
int pos = Vec3.createVec3(builder, 1.0f, 2.0f, 3.0f);
var pos = Vec3.CreateVec3(builder, 1.0f, 2.0f, 3.0f);
pos := sample.CreateVec3(builder, 1.0, 2.0, 3.0)
2 pos = MyGame.Sample.Vec3.CreateVec3(builder, 1.0, 2.0, 3.0)
var pos = MyGame.Sample.Vec3.createVec3(builder, 1.0, 2.0, 3.0);
$pos = \MyGame\Sample\Vec3::CreateVec3($builder, 1.0, 2.0, 3.0);
We have now serialized the non-scalar components of the orc, so we can serialize the monster itself:
int hp = 300;
int mana = 150;
auto orc = CreateMonster(builder, &pos, mana, hp, name, inventory, Color_Red,
weapons, Equipment_Weapon, axe.Union());
Monster.startMonster(builder);
Monster.addPos(builder, pos);
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);
int orc = Monster.endMonster(builder);
Monster.StartMonster(builder);
Monster.AddPos(builder, pos);
Monster.AddHp(builder, (short)300);
Monster.AddName(builder, name);
Monster.AddInventory(builder, inv);
Monster.AddColor(builder, Color.Red);
Monster.AddWeapons(builder, weapons);
Monster.AddEquippedType(builder, Equipment.Weapon);
Monster.AddEquipped(builder, axe.Value);
var orc = Monster.EndMonster(builder);
sample.MonsterStart(builder)
sample.MonsterAddPos(builder, pos)
sample.MonsterAddHp(builder, 300)
sample.MonsterAddName(builder, name)
sample.MonsterAddInventory(builder, inv)
sample.MonsterAddColor(builder, sample.ColorRed)
sample.MonsterAddWeapons(builder, weapons)
sample.MonsterAddEquippedType(builder, sample.EquipmentWeapon)
sample.MonsterAddEquipped(builder, axe)
orc := sample.MonsterEnd(builder)
2 MyGame.Sample.Monster.MonsterStart(builder)
3 MyGame.Sample.Monster.MonsterAddPos(builder, pos)
4 MyGame.Sample.Monster.MonsterAddHp(builder, 300)
5 MyGame.Sample.Monster.MonsterAddName(builder, name)
6 MyGame.Sample.Monster.MonsterAddInventory(builder, inv)
7 MyGame.Sample.Monster.MonsterAddColor(builder,
8 MyGame.Sample.Color.Color().Red)
9 MyGame.Sample.Monster.MonsterAddWeapons(builder, weapons)
10 MyGame.Sample.Monster.MonsterAddEquippedType(
11 builder, MyGame.Sample.Equipment.Equipment().Weapon)
12 MyGame.Sample.Monster.MonsterAddEquipped(builder, axe)
13 orc = MyGame.Sample.Monster.MonsterEnd(builder)
MyGame.Sample.Monster.startMonster(builder);
MyGame.Sample.Monster.addPos(builder, pos);
MyGame.Sample.Monster.addHp(builder, 300);
MyGame.Sample.Monster.addColor(builder, MyGame.Sample.Color.Red)
MyGame.Sample.Monster.addName(builder, name);
MyGame.Sample.Monster.addInventory(builder, inv);
MyGame.Sample.Monster.addWeapons(builder, weapons);
MyGame.Sample.Monster.addEquippedType(builder, MyGame.Sample.Equipment.Weapon);
MyGame.Sample.Monster.addEquipped(builder, axe);
var orc = MyGame.Sample.Monster.endMonster(builder);
\MyGame\Sample\Monster::StartMonster($builder);
\MyGame\Sample\Monster::AddPos($builder, $pos);
\MyGame\Sample\Monster::AddHp($builder, 300);
\MyGame\Sample\Monster::AddName($builder, $name);
\MyGame\Sample\Monster::AddInventory($builder, $inv);
\MyGame\Sample\Monster::AddColor($builder, \MyGame\Sample\Color::Red);
\MyGame\Sample\Monster::AddWeapons($builder, $weapons);
\MyGame\Sample\Monster::AddEquippedType($builder, \MyGame\Sample\Equipment::Weapon);
\MyGame\Sample\Monster::AddEquipped($builder, $axe);
$orc = \MyGame\Sample\Monster::EndMonster($builder);
Note: Since we 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 of adding a lot of fields that are only used in a small number of instances, as it will not bloat the buffer if unused.
If you do not wish to set every field in a
table
, it may be more convenient to manually set each field of your monster, instead of calling
CreateMonster()
. The following snippet is functionally equivalent to the above code, but provides a bit more flexibility.
MonsterBuilder monster_builder(builder);
monster_builder.add_pos(&pos);
monster_builder.add_hp(hp);
monster_builder.add_name(name);
monster_builder.add_inventory(inventory);
monster_builder.add_color(Color_Red);
monster_builder.add_weapons(weapons);
monster_builder.add_equipped_type(Equipment_Weapon);
monster_builder.add_equpped(axe);
auto orc = monster_builder.Finish();
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 these lines, to help highlight them more clearly:
monster_builder.add_equipped_type(Equipment_Weapon);
monster_builder.add_equipped(axe);
Monster.addEquippedType(builder, Equipment.Weapon);
Monster.addEquipped(axe);
Monster.AddEquippedType(builder, Equipment.Weapon);
Monster.AddEquipped(builder, axe.Value);
sample.MonsterAddEquippedType(builder, sample.EquipmentWeapon)
sample.MonsterAddEquipped(builder, axe)
1 MyGame.Sample.Monster.MonsterAddEquippedType(
2 builder, MyGame.Sample.Equipment.Equipment().Weapon)
3 MyGame.Sample.Monster.MonsterAddEquipped(builder, axe)
MyGame.Sample.Monster.addEquippedType(builder, MyGame.Sample.Equipment.Weapon);
MyGame.Sample.Monster.addEquipped(builder, axe);
\MyGame\Sample\Monster::AddEquippedType($builder, \MyGame\Sample\Equipment::Weapon);
\MyGame\Sample\Monster::AddEquipped($builder, $axe);
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.
builder.Finish(orc.Value);
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:
uint8_t *buf = builder.GetBufferPointer();
int size = builder.GetSize();
java.nio.ByteBuffer buf = builder.dataBuffer();
var buf = builder.DataBuffer;
buf := builder.FinishedBytes()
2 buf = builder.Output() // Of type `bytearray`.
var buf = builder.dataBuffer();
$buf = $builder->dataBuffer();
Reading Orc FlatBuffers
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 deserialize a FlatBuffer.
This seciton requires the same import/include, namespace, etc. requirements as before:
#include "monster_generate.h"
using namespace MyGame::Sample;
using FlatBuffers;
using MyGame.Sample;
import (
flatbuffers "github.com/google/flatbuffers/go"
sample "MyGame/Sample"
)
4 import MyGame.Sample.Any
5 import MyGame.Sample.Color
6 import MyGame.Sample.Monster
7 import MyGame.Sample.Vec3
var flatbuffers = require('/js/flatbuffers').flatbuffers;
var MyGame = require('./monster_generated').MyGame;
<script src="../js/flatbuffers.js"></script>
<script src="monster_generated.js"></script>
function __autoload($class_name) {
$class = substr($class_name, strrpos($class_name, "\\") + 1);
$root_dir = join(DIRECTORY_SEPARATOR, array(dirname(dirname(__FILE__))));
$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;
}
}
}
Then, assuming you have a variable containing to the bytes of data from disk, network, etc., you can create a monster from this data:
auto buffer_pointer = builder.GetBufferPointer();
auto monster = GetMonster(buffer_pointer);
java.nio.ByteBuffer buf = builder.dataBuffer();
Monster monster = Monster.getRootAsMonster(buf);
var buf = builder.DataBuffer;
var monster = Monster.GetRootAsMonster(buf);
buf := builder.FinishedBytes()
monster := sample.GetRootAsMonster(buf, 0)
6 monster = MyGame.Sample.Monster.Monster.GetRootAsMonster(buf, 0)
var buf = builder.dataBuffer();
var monster = MyGame.Sample.Monster.getRootAsMonster(buf);
$buf = $builder->dataBuffer();
$monster = \MyGame\Sample\Monster::GetRootAsMonster($buf);
If you look in the generated files from flatc
, you will see it generated accessors for all non-deprecated
fields. For example:
auto hp = monster->hp();
auto mana = monster->mana();
auto name = monster->name()->c_str();
short hp = monster.hp();
short mana = monster.mana();
String name = monster.name();
var hp = monster.Hp
var mana = monster.Mana
var name = monster.Name
hp := monster.Hp()
mana := monster.Mana()
name := string(monster.Name())
var hp = $monster.hp();
var mana = $monster.mana();
var name = $monster.name();
$hp = $monster->getHp();
$mana = $monster->getMana();
$name = monster->getName();
These should hold 300
, 150
, and "Orc"
respectively.
Note: We never stored a value in mp
, so we got the default value of 150
.
To access sub-objects, in the case of our pos
, which is a Vec3
:
auto pos = monster->pos();
auto x = pos->x();
auto y = pos->y();
auto z = pos->z();
Vec3 pos = monster.pos();
float x = pos.x();
float y = pos.y();
float z = pos.z();
var pos = monster.Pos
var x = pos.X
var y = pos.Y
var z = pos.Z
pos := monster.Pos(nil)
x := pos.X()
y := pos.Y()
z := pos.Z()
var pos = monster.pos();
var x = pos.x();
var y = pos.y();
var z = pos.z();
$pos = $monster->getPos();
$x = $pos->getX();
$y = $pos->getY();
$z = $pos->getZ();
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 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
.
auto inv = monster->inventory();
auto inv_len = inv->Length();
auto third_item = inv->Get(2);
int invLength = monster.inventoryLength();
byte thirdItem = monster.inventory(2);
int invLength = monster.InventoryLength;
var thirdItem = monster.GetInventory(2);
invLength := monster.InventoryLength()
thirdItem := monster.Inventory(2)
1 inv_len = monster.InventoryLength()
2 third_item = monster.Inventory(2)
var invLength = monster.inventoryLength();
var thirdItem = monster.inventory(2);
$inv_len = $monster->getInventoryLength();
$third_item = $monster->getInventory(2);
For vector
s of table
s, you can access the elements like any other vector, except your need to handle the result as a FlatBuffer table
:
auto weapons = monster->weapons();
auto weapon_len = weapons->Length();
auto second_weapon_name = weapons->Get(1)->name()->str();
auto second_weapon_damage = weapons->Get(1)->damage()
int weaponsLength = monster.weaponsLength();
String secondWeaponName = monster.weapons(1).name();
short secondWeaponDamage = monster.weapons(1).damage();
int weaponsLength = monster.WeaponsLength;
var secondWeaponName = monster.GetWeapons(1).Name;
var secondWeaponDamage = monster.GetWeapons(1).Damage;
weaponLength := monster.WeaponsLength()
weapon := new(sample.Weapon)
if monster.Weapons(weapon, 1) {
secondWeaponName := weapon.Name()
secondWeaponDamage := weapon.Damage()
}
1 weapons_length = monster.WeaponsLength()
2 second_weapon_name = monster.Weapons(1).Name()
3 second_weapon_damage = monster.Weapons(1).Damage()
var weaponsLength = monster.weaponsLength();
var secondWeaponName = monster.weapons(1).name();
var secondWeaponDamage = monster.weapons(1).damage();
$weapons_len = $monster->getWeaponsLength();
$second_weapon_name = $monster->getWeapons(1)->getName();
$second_weapon_damage = $monster->getWeapons(1)->getDamage();
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
).
auto union_type = monster.equipped_type();
if (union_type == Equipment_Weapon) {
auto weapon = static_cast<const Weapon*>(monster->equipped());
auto weapon_name = weapon->name()->str();
auto weapon_damage = weapon->damage();
}
int unionType = monster.EquippedType();
if (unionType == Equipment.Weapon) {
Weapon weapon = (Weapon)monster.equipped(new Weapon());
String weaponName = weapon.name();
short weaponDamage = weapon.damage();
}
var unionType = monster.EquippedType;
if (unionType == Equipment.Weapon) {
var weapon = (Weapon)monster.GetEquipped(new Weapon());
var weaponName = weapon.Name;
var weaponDamage = weapon.Damage;
}
unionTable := new(flatbuffers.Table)
if monster.Equipped(unionTable) {
unionType := monster.EquippedType()
if unionType == sample.EquipmentWeapon {
unionWeapon = new(sample.Weapon)
unionWeapon.Init(unionTable.Bytes, unionTable.Pos)
weaponName = unionWeapon.Name()
weaponDamage = unionWeapon.Damage()
}
}
1 union_type = monster.EquippedType()
3 if union_type == MyGame.Sample.Equipment.Equipment().Weapon:
6 union_weapon = MyGame.Sample.Weapon.Weapon()
7 union_weapon.Init(monster.Equipped().Bytes, monster.Equipped().Pos)
9 weapon_name = union_weapon.Name() //
'Axe'
10 weapon_damage = union_weapon.Damage() // 5
var unionType = monster.equippedType();
if (unionType == MyGame.Sample.Equipment.Weapon) {
var weapon_name = monster.equipped(new MyGame.Sample.Weapon()).name();
var weapon_damage = monster.equipped(new MyGame.Sample.Weapon()).damage();
}
$union_type = $monster->getEquippedType();
if ($union_type == \MyGame\Sample\Equipment::Weapon) {
$weapon_name = $monster->getEquipped(new \MyGame\Sample\Weapon())->getName();
$weapon_damage = $monster->getEquipped(new \MyGame\Sample\Weapon())->getDamage();
}
Mutating FlatBuffers
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:
auto monster = GetMutableMonster(buffer_pointer);
monster->mutate_hp(10);
monster->mutable_pos()->mutate_z(4);
monster->mutable_inventory()->Mutate(0, 1);
Monster monster = Monster.getRootAsMonster(buf);
monster.mutateHp(10);
monster.pos().mutateZ(4);
monster.mutateInventory(0, 1);
var monster = Monster.GetRootAsMonster(buf);
monster.MutateHp(10);
monster.Pos.MutateZ(4);
monster.MutateInventory(0, 1);
<API for mutating FlatBuffers is not yet available in Go.>
1 <API
for mutating FlatBuffers
is not yet available
in Python.>
<API for mutating FlatBuffers is not yet support in JavaScript.>
<API for mutating FlatBuffers is not yet supported in PHP.>
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 that 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.
JSON with FlatBuffers
Using flatc
as a Conversion Tool
This is often the preferred method to use JSON with FlatBuffers, 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).
Lets 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,
y: 2,
z: 3
},
hp: 300,
name: "Orc"
}
You can run this file through the flatc
compile with the -b
flag and our monster.fbs
schema to produce a FlatBuffer binary file.
./../flatc -b 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.
Note: If you're working in C++, you can also parse JSON at runtime. See the Use in C++ section of the Programmer's Guide for more information. Advanced Features for Each Language
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: