ProtoBuf.js 如何在编码对象时过滤额外字段

ProtoBuf.js how to filter extra fields when encoding an object

我正在使用 Protobuf.js 为 sails.js 编写一个挂钩,以便能够发送由 socket.io 协议缓冲区编码的消息。

假设我有一个原型方案Message.proto:

message Message{
    required string message = 1;
    optional string user = 2;
    optional int32 id = 3;
}

我正在从数据库中获取一个对象 data:

data = { 
  message: 'Hello',
  user: 'johnDoe',
  id: 90,
  createdAt: '2015-10-16T10:15:39.837Z',
  updatedAt: '2015-10-16T10:15:39.837Z' 
};

我有一个 sails hook,代码为:

var ProtoBuf = require("protobufjs"),
    Reflect = ProtoBuf.Reflect,
    builder = ProtoBuf.newBuilder(),
    protoModels;

...

    // This method overrides socket.io broadcast call for sails.js models pubSub:
    augmentModels: function () {
        for (var identity in app.models) {
            var protobufSchemeName = app.models[identity].protobufSchemeName;
            // protobufSchemeName is the file name of the sheme
            if (protobufSchemeName) {
                ProtoBuf.loadProtoFile(path.join(app.config.appPath, app.config.protobuf.folder, protobufSchemeName + ".proto"), builder);
                app.models[identity].broadcast = function (roomName, eventName, data, socketToOmit) {
                    var dataToSend = protoModels[protobufSchemeName].encode(???HERE???); //HERE I SHOULD PUT THE MODIFIED DATA WITH THE ONLY NECESSARY FIELDS
                    app.sockets.broadcast(roomName, eventName, dataToSend.toBuffer(), socketToOmit);
                };
            }
            protoModels = builder.build();
        }
    }

我不能这样直接传递dataprotoModels[protobufSchemeName].encode(data) 因为它会导致错误:this+"#"+keyOrObj+" is not a field,因为数据包含额外的字段;

所以问题来了:什么是仅保存到dataToSend方案中的字段的最简单和正确的方法(Message.proto) ?

P.S。我知道 Reflection 应该有一些东西,他们说你可以这样做:

var TPlayer = builder.lookup("Game.Player"); // instance of ProtoBuf.Reflect.Message

var fields = TPlayer.getChildren(ProtoBuf.Reflect.Message.Field); // instances of ProtoBuf.Reflect.Message.Field
fields.forEach(function(field) {
    //Filter it here
});

但我不知道如何从 protoModels 到达 lookup 方法。也许有办法使用 Builder's isMessageField 方法来过滤 data?

中的字段

我们那里还有一个生成器,所以解决方案与 example 中的完全相同:

...
augmentModels: function () {
        for (var identity in app.models) {
            var protobufSchemeName = app.models[identity].protobufSchemeName;
            if (protobufSchemeName) {
                ProtoBuf.loadProtoFile(path.join(app.config.appPath, app.config.protobuf.folder, protobufSchemeName + ".proto"), builder);
                app.models[identity].broadcast = function (roomName, eventName, data, socketToOmit) {
                    var fields = builder.lookup(protobufSchemeName).getChildren(ProtoBuf.Reflect.Message.Field).map(function (f) {
                        return f.name;
                    }); //Here are the fieldnames of the scheme as an array
                    var dataToSend = protoModels[protobufSchemeName].encode(_.pick(data, fields)).toBuffer();
                    app.sockets.broadcast(roomName, eventName, dataToSend, socketToOmit);
                };
            }
            protoModels = builder.build();
        }
    }

另一种方法是覆盖 emit/broadcast 方法,就像我在下面的钩子中所做的那样:

P.S。如果有人觉得这有用:

我为 Sails.js 制作了一个 Protobuf Serialization Hook。欢迎投稿!