javascript - deserialisera google flat buffers


Jag har en Node.js-applikation som använder Google FlatBuffers.

fbs-filen för dryckesschemat:

namespace MyAlcoholist;

table Drink {
drink_type_name: string;
drink_company_name: string;
drink_brand_name: string;
drink_flavor_type_name : string;
liquid_color_type_name : string;
liquid_color_is_transparent : bool;
alcohol_vol : float;
calories_for_100g : uint;
global_image_id: uint;
drink_flavor_id: uint;
}

table Drinks { drinks:[Drink]; }

root_type Drinks;

kompilerade det med flatc -s drinks.fbs, det genererade js-filen drinks_generated.js. hittills så bra.

Jag använder följande kod i min Node.js-server för att förbereda och skapa flatbuffern från en array:

flatBuffersUtil.js

var flatbuffers = require('../js/flatbuffers').flatbuffers;
var builder = new flatbuffers.Builder();

var drinks = require('../fbs/drinks_generated').MyAlcoholist; // Generated by `flatc`.

function drinkArrayToBuffer(drinkArray) {
var drinksVectArray = [];
drinkArray.forEach(function (element, index, array) {
    var drinkObj = element;
    var drinkBrandName = builder.createString(drinkObj.drink_brand_name);
    var drinkCompanyName = builder.createString(drinkObj.drink_company_name);
    var drinkflavorTypeName = builder.createString(drinkObj.drink_flavor_type_name);
    var drinkTypeName = builder.createString(drinkObj.drink_type_name);
    var liquidColorTypeName = builder.createString(drinkObj.liquid_color_type_name);
    drinks.Drink.startDrink(builder);
    drinks.Drink.addAlcoholVol(builder, drinkObj.alcohol_vol);
    drinks.Drink.addCaloriesFor100g(builder, drinkObj.calories_for_100g);
    drinks.Drink.addDrinkBrandName(builder, drinkBrandName);
    drinks.Drink.addDrinkCompanyName(builder, drinkCompanyName);
    drinks.Drink.addDrinkFlavorId(builder, drinkObj.drink_flavor_id);
    drinks.Drink.addDrinkFlavorTypeName(builder, drinkflavorTypeName);
    drinks.Drink.addDrinkTypeName(builder, drinkTypeName);
    drinks.Drink.addGlobalImageId(builder, drinkObj.global_image_id);
    drinks.Drink.addLiquidColorIsTransparent(builder, drinkObj.is_transparent);
    drinks.Drink.addLiquidColorTypeName(builder, liquidColorTypeName);
    var drink = drinks.Drink.endDrink(builder);
    drinksVectArray.push(drink);
})
var drinksVect = drinks.Drinks.createDrinksVector(builder, drinksVectArray);
builder.finish(drinksVect);
var buf = builder.dataBuffer();
var drinksArray = drinks.Drinks.getRootAsDrinks(buf);
return buf;
}

module.exports.drinkArrayToBuffer = drinkArrayToBuffer;

användning av flatBuffersUtil.js:

...
var data = flatBuffersUtil.drinkArrayToBuffer(result);
res.send(data);

klienten är klar med angularjs 1.5.0 när jag tar emot bufferten försöker jag skapa ett objekt av den med följande kod:

i huvudindexfilen:

<script type="text/javascript" src="js/flatbuffers.js"></script>
<script type="text/javascript" src="js/fbs/drinks_generated.js"></script>

då i Angular-kontrollern:

           $http({
                data: data,
                method: 'POST',
                url: 'https://myalcoholist.com:8888/drink/get_list/all_drinks',
            }).then(function successCallback(response) {
                var buffer = response.data;
                var drinks = MyAlcoholist.Drinks.getRootAsDrinks(buffer);
                deferred.resolve(drinks);
                // this callback will be called asynchronously
                // when the response is available
            }, function errorCallback(response) {
                // called asynchronously if an error occurs
                // or server returns response with an error status.
            });

problemet är att funktionen MyAlcoholist.Drinks.getRootAsDrinks(buffer); misslyckas med följande fel:

TypeError: bb.position is not a function
at Function.MyAlcoholist.Drinks.getRootAsDrinks (drinks_generated.js:256)
at successCallback (admin-drinks-controller.js:22)
at angular.js:15552
at m.$eval (angular.js:16820)
at m.$digest (angular.js:16636)
at m.$apply (angular.js:16928)
at g (angular.js:11266)
at t (angular.js:11464)
at XMLHttpRequest.u.onload (angular.js:11405)

nu.. funktionens kod i den genererade filen drinks_generated.js är följande:

MyAlcoholist.Drinks.getRootAsDrinks = function(bb, obj) {
return (obj || new MyAlcoholist.Drinks).__init(bb.readInt32(bb.position()) + bb.position(), bb);
};

så bb ska vara av typen bytebuffer med funktionen position(), men det jag får är ett objekt av bytes. Jag antar att jag måste typomvandla det först eller på något sätt... men detta är bara en gissning.

några idéer?

uppdatera

ok först reviderade jag min drinksArrayToBuffer-funktion till följande kod:

function drinkArrayToBuffer(drinkArray) {
var drinksVectArray = [];
drinkArray.forEach(function (element, index, array) {
    var drinkObj = element;
    var drinkBrandName = builder.createString(drinkObj.drink_brand_name);
    var drinkCompanyName = builder.createString(drinkObj.drink_company_name);
    var drinkflavorTypeName = builder.createString(drinkObj.drink_flavor_type_name);
    var drinkTypeName = builder.createString(drinkObj.drink_type_name);
    var liquidColorTypeName = builder.createString(drinkObj.liquid_color_type_name);
    drinks.Drink.startDrink(builder);
    drinks.Drink.addAlcoholVol(builder, drinkObj.alcohol_vol);
    drinks.Drink.addCaloriesFor100g(builder, drinkObj.calories_for_100g);
    drinks.Drink.addDrinkBrandName(builder, drinkBrandName);
    drinks.Drink.addDrinkCompanyName(builder, drinkCompanyName);
    drinks.Drink.addDrinkFlavorId(builder, drinkObj.drink_flavor_id);
    drinks.Drink.addDrinkFlavorTypeName(builder, drinkflavorTypeName);
    drinks.Drink.addDrinkTypeName(builder, drinkTypeName);
    drinks.Drink.addGlobalImageId(builder, drinkObj.global_image_id);
    drinks.Drink.addLiquidColorIsTransparent(builder, drinkObj.is_transparent);
    drinks.Drink.addLiquidColorTypeName(builder, liquidColorTypeName);
    var drink = drinks.Drink.endDrink(builder);
    drinksVectArray.push(drink);
})
var drinksVect = drinks.Drinks.createDrinksVector(builder, drinksVectArray);
drinks.Drinks.startDrinks(builder);
drinks.Drinks.addDrinks(builder, drinksVect);
var endDrinksOffset = drinks.Drinks.endDrinks(builder);
drinks.Drinks.finishDrinksBuffer(builder, endDrinksOffset);
var buf = builder.dataBuffer();
return buf;
}

nu på serversidan kan jag deserialisera bufferten och använda datan korrekt. Problemet är med att deserialisera på klientsidan.

efter att jag skickar bufferten, tar klientsidan emot följande objekt:

Object
 bytes_: Object  
 position_: 7148
 __proto__: Object 

nu.. jag skapade följande funktion för att konvertera bufferten till ett json-objekt på klientsidan:

function drinksByteArrayToArray(buffer) {
var res = [];
var byteBuffer =new flatbuffers.ByteBuffer(buffer);
var drinks = MyAlcoholist.Drinks.getRootAsDrinks(byteBuffer);
var drinksLength = drinks.drinksLength();
for (var i=0;i<drinksLength;i++) {
    var drink = drinks.drinks(i);
    var drinkObj = {
        drink_flavor_id: drink.drinkFlavorId(),
        drink_type_name: drink.drinkTypeName(),
        drink_company_name: drink.drinkCompanyName(),
        drink_brand_name: drink.drinkBrandName(),
        drink_flavor_type_name: drink.drinkFlavorTypeName(),
        liquid_color_type_name: drink.liquidColorTypeName(),
        is_transparent: drink.liquidColorIsTransparent(),
        alcohol_vol: drink.alcoholVol(),
        calories_for_100g: drink.caloriesFor100g(),
        global_image_id: drink.globalImageId
    }
    res.push(drinkObj);
}
return res;
}

när jag kör den här funktionen är längden på drinks noll. så bufferten verkar vara tom.

nu tror jag att problemet ligger i följande kod:

var byteBuffer =new flatbuffers.ByteBuffer(buffer);

Jag tror att jag behöver konvertera datan till bytebuffer på något annat sätt.

Jag förstod att jag behöver ge flatbuffers.ByteBuffer en array av bytes, så jag försökte göra följande sak:

var byteBuffer =new flatbuffers.ByteBuffer(buffer.bytes_);

men resultaten är desamma.

några idéer?

Svar #1

tack för ditt stöd. jag kunde äntligen komma fram till en lösning. det jag saknade var att använda setPosition på byteBuffer efter att ha satt dess bytes. Varför? jag vet inte! allt jag vet är att jag provade det och nu fungerar det.

så jag skickar hela bufferten som innehåller bytes_ för datan och position_ för... jag antar... längden eller något. Så efter att jag har skickat bufferten på klientsidan utför jag dessa två kommandon:

var byteBuffer =new flatbuffers.ByteBuffer(buffer.bytes_);
byteBuffer.setPosition(buffer.position_);

om någon kan hjälpa till här med att ge mer information om varför jag behöver göra det, skulle det vara toppen :) hur som helst... det fungerar, jippie!

uppdatera

Jag öppnade ett ärende på https://github.com/google/flatbuffers/issues/3781 och jag fick mitt svar :)

Det finns exempel i https://github.com/google/flatbuffers/blob/master/tests/JavaScriptTest.js. För att serialisera en flatbuffer:

var fbb = new flatbuffers.Builder();
// ... build the flatbuffer ...
var uint8Array = fbb.asUint8Array();

För att deserialisera en flatbuffer:

var bb = new flatbuffers.ByteBuffer(uint8Array);
var drinks = MyAlcoholist.Drinks.getRootAsDrinks(bb);

tack!