使用

英文原地址

因为 JavaScript 是一种动态类型语言,为保证最佳的性能(以及作为副产品的类型验证),protobuf.js引入了有效信息(valid message)的概念:

有效信息

有效信息是指一个对象: (1) 不会遗漏任何必需字段。 (2) 仅由有线格式(wire format)编写者能理解的 JS 类型组成。

有两种可能的有效信息类型和编码器能够实现这种情况:

  • 信息实例(原型上有默认值的信息类的显式实例)始终(必须)满足有效的信息需求。
  • 简单对象(plain JavaScript object)恰好以满足有效信息所需的方式组合而成。

简而言之,有线格式编写者能够理解以下类型:

字段类型 期望的 JS 类型(创建、编码) (从对象)转换
s-/u-/int32
s-/fixed32
number(32位整数) 如果有符号value | 0
如果没有符号value >>> 0
s-/u-/int64
s-/fixed64
Long(最佳)
number(53 位整数)
long.jsLong.fromValue(value)
或者是parseInt(value, 10)
float
double
number Number(value)
bool boolean Boolean(value)
string string String(value)
bytes Uint8Array(最佳)
Buffer(node 环境最佳)
Array.<number>(8 位整数)
如果是stringbase64.decode(value)
带有非零的.length属性的Object会被认为是类 buffer 对象。
enum number(32位整数) 如果是string,则查找数字 id
message 有效信息 Message.fromObject(value)
  • 如果字段是可选字段,则将显式的undefinednull视为未设置。
  • 重复字段为Array.<T>
  • 映射字段是带有相应值是字符串表示键的Object.<string,T>,或者是类Long类型的8个字符长的二进制散列字符串。
  • 表格中标出“最佳”的类型性能最好,因为不需要任何转换步骤(如数字向低位或高位转换,或 base64 字符串转换为 buffer)。

工具箱

出于性能原因,每个message类都提供了一组不同的方法,各司其职。这避免了性能方面的不必要断言/冗余操作,但也迫使用户在必要时主动执行验证(对于可能恰好是有效信息的简单对象) ———— 比方说在处理用户输入的时候。

注意,下面的Message指任意message类。

  • Message.verify(message: Object): null|string
    验证普通 JavaScript 对象是否满足有效信息需求,从而可以无顾虑地进行编码。它不抛出错误消息,而是以字符串的形式返回错误消息(如果有的话)。
var payload = "invalid (not an object)";
var err = AwesomeMessage.verify(payload);
if (err)
  throw Error(err);
1
2
3
4
  • Message.encode(message: Message|Object [, writer: Writer]): Writer
    信息实例简单 JavaScript 对象进行编码。该方法并不隐式验证信息,对于信息的有效性是由用户来保证的。
var buffer = AwesomeMessage.encode(message).finish();
1
  • Message.encodeDelimited(message: Message|Object [, writer: Writer]): Writer
    Message.encode类似,但是以varint形式添加了信息的长度。
  • Message.decode(reader: Reader|Uint8Array): Message
    将 buffer 解码为信息实例。如果缺少必需的字段,则会抛出一个util.ProtocolError,并将instance属性设置为到目前为止已解码的信息。如果有线格式(wire format)无效,则会抛出一个Error
try {
  var decodedMessage = AwesomeMessage.decode(buffer);
} catch (e) {
    if (e instanceof protobuf.util.ProtocolError) {
      // e.instance 保存了到目前为止除必需字段外的解码消息
    } else {
      // 有线格式无效的话
    }
}
1
2
3
4
5
6
7
8
9
  • Message.decodeDelimited(reader: Reader|Uint8Array): Message
    Message.decode类似,但是以varint形式读取信息的长度。
  • Message.create(properties: Object): Message
    根据满足有效信息的一组属性中创建新的信息实例。若可以,建议首选Message.create而非Message.fromObject,因其可能不会执行冗余转换。
var message = AwesomeMessage.create({ awesomeField: "AwesomeString" });
1
  • Message.fromObject(object: Object): Message
    使用上表中列出的转换步骤将任何无效的简单对象转换为信息实例
var message = AwesomeMessage.fromObject({ awesomeField: 42 });
// 将 awesomeField 转换为字符串
1
2
  • Message.toObject(message: Message [, options: ConversionOptions]): Object
    信息实例转换为任意的简单 JavaScript 对象,以便与其他库或存储进行互操作。根据设置好的转换配置项,最终得到的简单 JavaScript 对象也许会仍然满足有效信息的要求,但大多数情况并不会这样。
var object = AwesomeMessage.toObject(message, {
  enums: String,  // 以字符串名称进行枚举
  longs: String,  // 将 longs 作为字符串 (需要 long.js)
  bytes: String,  // 将字节作为 base65 编码的字符串
  defaults: true, // 包含默认值
  arrays: true,   // 填充空数组(重复字段),即使 defaults = false
  objects: true,  // 填充空对象(映射字段),即使 defaults = false
  oneofs: true    // 是否包括设置为当前字段名称的字段的虚拟字段
});
1
2
3
4
5
6
7
8
9

下面的图显示了不同方法之间的关系以及有效消息的概念,以供参考:

换句话说:verify表明在简单对象上直接调用createencode将各自成功地转换有效信息。另一方面,fromObject则从更广泛的简单对象进行转换以创建有效信息。参考