Start in protobuf

Protocol Buffers are a language-neutral, platform-neutral, extensible way of serializing structured data.

Basic usage

Using JSON descriptors

1
2
3
4
5
6
7
8
9
10
11
12
13
// awesome.json
{
"nested": {
"AwesomeMessage": {
"fields": {
"awesomeField": {
"type": "string",
"id": 1
}
}
}
}
}
Type(T) Extends Type-specific properties
Root Namespace nested
Type Namespace fields
Enum ReflectionObject values
Field ReflectionObject rule, type, id
MapField Field keyType
OneOf ReflectionObject oneof(array of field names)
Service Namespace methods
Method ReflectionObject type, requestType,responseType,requestStream,responseStream
  • T#fromJSON(name, json) creates the responsive reflection object JSON descriptor
  • T#toJSON() creates a JSON descriptor from the responsive reflection object

Using reflection only

1
2
3
4
5
6
7
const Root = protobuf.Root
const Type = protobuf.Type
const Field = protobuf.Field

const AwesomeMessage = new Type('AwesomeMessage').add(new Field('awesomeField', 1, 'string'))

const root = new Root().define('awesomepackage').add(AwesomeMessage)

Inspect the protobuf.js

The code snippets following have been converted into class-style.

Protobuf ReflectionObject

Located at src/object.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
let Root

class ReflectionObject {
name: string // unique name within its namespace
options: object
parent: string | null = null // parent namespace
resolved: boolean = false // whether already resolved or not
comment: string | null = null // comment text, if any
filename: string | null = null // defining file name

constructor(name: string, options: object) {
if (!isString(name)) throw new TypeError('name must be a string')
this.name = name
if (!isObject(options)) throw new TypeError('options must be an object')
this.options = options
}

/**
* @property root
* @description Reference to the root namespace
*/
root: {
get() {
let ptr = this;
while(ptr.parent !== null) {
ptr = ptr.parent
}
return ptr
}
}

/**
* @property fullName
* @description full name including leading dot.
*/
fullName:{
get() {
let path = [this.name]
let ptr = this.parent
while(ptr) {
path.unshift(ptr.name)
ptr = ptr.parent
}
return path.join('.')
}
}

/**
* @method toJSON
* @description Converts this reflection object to its descriptor representation
*/
toJSON() {
throw Error() // not implemented, shouldn't happen
}

/**
* @method onAdd
* @description called when this object is added to a parent
*/
onAdd(parent) {
if (this.parent && this.parent !== parent) {
this.parent.remove(this)
}
this.parent = parent
this.resolved = false
let root = parent.root
if (root instanceof Root) {
root._handleAdd(this)
}
}

/**
* @method onRemove
* @description called when this object is removed from a parent
*/
onRemove(parent) {
let root = parent.root
if (root instanceof Root) {
root._handleRemove(this)
}
this.parent = null
this.resolved = false
}

/**
* @method resolve
* @description resovles this objects type references
*/
resolve() {
if (this.resolved) {
return this
}
if (this.root instanceof Root) {
this.resolved = true
}
return this
}

/**
* @method getOption
* @description gets an option value
*/
getOption(name) {
if (this.options) {
return this.options[name]
}
return undefined
}

/**
* @method set an option
*/
setOption(name, value, ifNotSet) {
if (!ifNotSet || !this.options || this.options[name]===undefined) {
(this.options || this.options = {})[name] = valeu
}
return this
}

setOptions(options, ifNotSet) {
if (options) {
Object.keys(options).forEach(name => {
this.setOption(name, options[name], ifNotSet)
})
}
return this
}

toString() {
const className = this.constructor.className
const fullName = this.fullName
if (fullName.length) {
return className + ' ' + fullName
}
return className
}

/**
* @description set up cyclic dependencies(called in index-light)
*/
_configure = (Root_) {
Root = Root_
}
}

Protobuf Namespace

Located at src/namespace.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
let Type, Service, Enum

/**
* @function arrayToJSON
* @description convert an array of reflection objects to JSON
*/
function arrayToJSON(array, toJSONOptions) {
if (!(array && array.length)) {
return undefined
}
let obj = {}
array.forEach(reflection => {
obj[reflection.name] = reflection.toJSON(toJSONOptions)
})
return obj
}

function clearCache(namepsace: Namespace) {
namespace._nestedArray = null
return namespace
}

class Namespace extends ReflectionObject {
nested: boolean = undefined // nested objects by name
_nestedArray: ReflectionObject[] | null = null // cached nested objects as an array

nestedArray:{
get() {
return this._nestedArray || (this._nestedArray = util.toArray(this.nested))
}
}

static arrayToJSON = arrayToJSON

/**
* @mehtod fromJSON
* @description construct a namespace from JSON
*/
static fromJSON(name, json) {
return new Namespace(name, json.options).addJSON(json.nested)
}


/**
* @mehtod isReservedId
* @description test if the specified id is reserved
*/
static isReservedId(reserved, id) {
if (reserved) {
for (let i = 0; i < reserved.length; ++i) {
if (typeof reserved[i] !== 'string' && reserved[i][0] <= id && reserved[i][1] > id) {
return true
}
}
}
return false
}

/**
* @method isReservedName
* @description test if the specified name is reserved
*/
isReservedName(reserved, name) {
if (reserved) {
for (let i = 0; i < reserved.length; ++i) {
if (reserved[i] === name) {
return true
}
}
}
return false
}

constructor(name, options) {
super(name, options)
}

/**
* @method toJSON
* @description convert this namespace to a namespace descriptor
*/
static toJSON(toJSONOptions) {
return util.toObject([
'options': this.options,
'nested': arrayToJSON(this.nestedArray, toJSONOptions)
])
}

/**
* @method addJSON
* @description Add nested objects to this namespace from nested object descriptors
*/
addJSON(nestedJSON) {
const ns = this
if (nestedJSON) {
for (let names = Object.keys(nestedJSON), i = 0, nested; i< names.length; ++i) {
nested = nestedJSON[names[i]]
ns.add(
(nested.fields !== undefined ? Type.fromJSON : nested.values !== undefined ? Enum.fromJSON : nested.methods !== undefined ? Service.fromJSON:nested.id !== undefined ? Field.fromJSON : Namespace.fromJSON)(names[i], nested)
)
}
}
return this
}

get(name) {
return this.nested && this.nested[name] || null
}

getEnum(name) {
if (this.nested && this.nested[name] instanceof Enum) {
return this.nested[name].value
}
throw new Error(`no such enum: ${name}`)
}

/**
* @mehtod add
* @description add a nested object to this namespace
*/
add(object) {
if (!(object instanceof Field && object.extend !== undefined || object instanceof Type || object instanceof Enum || object instanceof Servce || object instanceof Namespace)) {
throw new TypeError('object must be a valid nested object')
}

if (!this.nested){
this.nested = {}
} else {
let prev = this.get(object.name)
if (prev) {
if (prev instanceof Namespace && object instanceof Namespace && !(prev instanceof Type || prev instanceof Service)) {
let nested = prev.nestedArray
for (let i = 0; i < nested.length; ++i) {
object.add(nested[i])
}
this.remove(prev)
if (!this.nested) {
this.nested = {}
}
object.setOptions(prev.options, true)
} else {
throw new Error(`duplicate name ${object.name} in ${this}`)
}
}
}
this.nested[object.name] = object
object.onAdd(this)
return clearCache(this)
}
}

Protobuf Root

Protobuf Enum

Protobuf Type

Protobuf Field

Protobuf OneOf

Protobuf MapField

Protobuf Service

Protobuf Method

1