Teil I: Protocol Buffers
Du hast eine neue Idee für eine App. Diese soll stark mit einem Webserver kommunizieren? Viele würden nun anfangen eine RESTful–Api zu schreiben. Schön altmodisch. Vielleicht etwas aufgefrischt durch Swagger. Doch letzten Endes eine JSON Ausgabe der Datenbankabfrage. Lesbar für Mensch wie Maschine. Wozu was ändern?
REST ist wunderbar. Viele verschiedene Klienten können auf eine einheitliche API zurückgreifen. Einfach eine URL öffnen und ein JSON auslesen. Doch REST ist auch langsam. Es wird über ein unkomprimiertes Http/1.1 verteilt. Des Weiteren müssen die Daten erst in ein JSON Format und anschließend wieder aus diesem JSON Format in Variablen oder Objekte der benutzten Sprache zurückübersetzt werden. Außerdem willst du vielleicht nicht, dass jeder deine API auslesen kann. Immerhin ist es das Backend für deine App.
Die Lösung heißt RPC. Es ist schnell, da komprimiert und sogar möglich zu verschlüsseln. RPC läuft über das moderne Http/2.0. Es ist Binär und wie CORBA sprachunabhängig. Ein Service API mit RPC kann nur ohne weiteres von Anwendungen benutzt werden, die das zugrundeliegende Interface kennen.
Das Interface das wir verwenden werden sind die Google Protocol Buffers.
Was sind Protocol Buffers?
Protocol Buffers sind eine plattformunabhängige Sprache zur Seriallisierung strukturierter Datet. Sie sind zu vergleichen mit XML. Jedoch kleiner, schneller und einfacher (laut Google). Mit ihnen ist es also möglich Stubs für Klassen zu erstellen um sie zwischen verschiedenen System zu teilen.
Ein kleines Beispiel:
syntax = "proto3"; // 1. Syntax definieren package net.rantzen; // 2. Strukturieren // 3. Message als Klassen Stub message Test { uint64 _id = 1; // 4. Geordnete Variablen im Stub string text = 2; }
- Als erstes wird der Syntax definiert. Die neuste Version ist 3, daher benutzen wir ausschließlich Proto3 und kümmern uns nicht um Rückwärtskompatibilität, da es sich hier um ein neues Projekt handelt.
- Wie man es z.B. aus Java kennt, definiert man ein package in dem sich der Stub befindet. Dadruch kann man den gleichen Namen für eine Message in verschiedenen Packages verwendet.
- Der eigentliche Stub der Klasse wird als Message definiert. Es ist möglich mehrere Messages in der gleichen Datei zu defnieren und sogar Messages innerhalb von Messages als geschachtelte Stubs. Dach dazu später mehr.
- Die einzelnen Variablen einer Message werden nummeriert und ihr Typ definiert. Eine Liste der möglichen Typen kann man hier lesen.
Erstellen eines Protocol Buffers Projekts
Am besten ist es immer etwas zu lernen, indem man sich ein Projekt vorstellt. Nehmen wir an, das wir eine App haben, wo Benutzer ihre Gedanken niederschreiben können. Eine simple Note App. Die Notes werden an einen Server gesendet und können auch vom Server wieder gelesen werden. Also eine simple Note App mit Backup bzw. Cloud-Sync. Funktion.
Was brauchen wir?
- Ein Benutzer: User.proto
- Eine Note: Note.proto
Unser Benutzer braucht eine ID um ihn intern zu identifizieren. Dazu eine Email-Addresse und ein Passwort damit er wieder auf seine Daten zugreifen kann.
Seine Notes brauchen natürlich ein Text, wo die eigentliche Note steht. Eine ID zur internen Verarbeitung und verschiedene Datum Angaben. Ein Erstell-Datum und ein Update-Datum. Dazu noch ein Typ. Ob es eine neue Idee ist, oder eine Erinnerung.
Wir erstellen also zwei Stubs im Proto3 Syntax:
Note.proto
syntax = "proto3"; package net.rantzen; import "google/protobuf/timestamp.proto"; message Note { uint64 _id = 1; string text = 2; Date dates = 3; Type type = 4; enum Type { IDEA = 0; REMINDER = 1; } message Date { google.protobuf.Timestamp created = 1; google.protobuf.Timestamp updated = 2; } }
In unserer Note.proto Datei sind die Daten in einer geschachtelten Stub Date gespeichert. In diesem Fall unnötig aber schön zum Zeigen, dass die Numerierung nicht in die Unterstubs übertragen wird. Sie fangen wieder bei 1 an. Des Weiteren importieren wir das Google Stub Timestamp für unser Datum. Der Typ wird als Enum repräsentiert. Enums fangen bei 0 an, nicht bei 1, da sie auch später diesen Wert haben werden in der realen Klasse.
User.proto
syntax = "proto3"; package net.rantzen; import "Note.proto"; message User { uint64 _id = 1; string email = 2; string password = 3; repeated Note notes = 4; }
In der User.proto gehen wir ähnlich wie bei der Note.proto vor. Diesesmal importieren wir jedoch das vorhin erstellte Note.proto und erstellen einen Eintrag Note notes. Davor setzen wir das Wort repeated. Dies führt dazu, dass dies ein Array von Notes wird.
Kompilierung der Stubs
Protocol Buffers sind Sprach-Neutral. Es gibt nativ unterstützte Sprachen und sehr viele Compiler von der Community geschrieben. In diesem Beispiel sind wir an JavaScript interisiert. JavaScript bietet keine Optionen. Andere Sprachen brauchen einen. Für Java z.B. brauchen unsere Stubs noch einen Eintrag für das Java package und den Klassenname:
option java_package = "net.rantzen.protobuf"; option java_outer_classname = "UserProto";
Installation der Compiler
Um unsere Dateien Stubs zu kompilieren, müssen wir zuerst den Compiler installieren. Diesen kann man hier finden. Ist der Kompiler installiert und die Path-Variable gesetzt, kann es weiter gehen.
Kompilierung
Da wir JavaScript haben wollen rufen wir den Compiler mit diesen Argumenten auf:
protoc -I=Proto/ --js_out="Proto/out/" User.proto Note.proto
Dies setzt vorraus, dass wir eine Filestruktur haben die so aussieht:
Proto/ | out/ | User.proto | Note.proto
Mit -I wird der Ort übergeben wo unsere Proto Dateien liegen. –js_out sagt, dass wir eine JavaScript Datei haben wollen in dem Proto/out/ Ordner. Dieser muss schon vorhanden sein. Der Kompiler ist nicht in der Lage Ordner zu erstellen. Schließlich kommen die beiden Dateien die wir kompilieren wollen: User.proto Note.proto.
Dadurch erhalten wir nun zwei JavaScript Dateien die wir verwenden können.
[su_spoiler title=”JavaScript Code”]
/** * @fileoverview * @enhanceable * @suppress {messageConventions} JS Compiler reports an error if a variable or * field starts with 'MSG_' and isn't a translatable message. * @public */ // GENERATED CODE -- DO NOT EDIT! goog.provide('proto.net.rantzen.Note'); goog.provide('proto.net.rantzen.Note.Date'); goog.provide('proto.net.rantzen.Note.Type'); goog.require('jspb.BinaryReader'); goog.require('jspb.BinaryWriter'); goog.require('jspb.Message'); goog.require('proto.google.protobuf.Timestamp'); /** * Generated by JsPbCodeGenerator. * @param {Array=} opt_data Optional initial data array, typically from a * server response, or constructed directly in Javascript. The array is used * in place and becomes part of the constructed object. It is not cloned. * If no data is provided, the constructed object will be empty, but still * valid. * @extends {jspb.Message} * @constructor */ proto.net.rantzen.Note = function(opt_data) { jspb.Message.initialize(this, opt_data, 0, -1, null, null); }; goog.inherits(proto.net.rantzen.Note, jspb.Message); if (goog.DEBUG && !COMPILED) { proto.net.rantzen.Note.displayName = 'proto.net.rantzen.Note'; } if (jspb.Message.GENERATE_TO_OBJECT) { /** * Creates an object representation of this proto suitable for use in Soy templates. * Field names that are reserved in JavaScript and will be renamed to pb_name. * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. * For the list of reserved names please see: * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. * @param {boolean=} opt_includeInstance Whether to include the JSPB instance * for transitional soy proto support: http://goto/soy-param-migration * @return {!Object} */ proto.net.rantzen.Note.prototype.toObject = function(opt_includeInstance) { return proto.net.rantzen.Note.toObject(opt_includeInstance, this); }; /** * Static version of the {@see toObject} method. * @param {boolean|undefined} includeInstance Whether to include the JSPB * instance for transitional soy proto support: * http://goto/soy-param-migration * @param {!proto.net.rantzen.Note} msg The msg instance to transform. * @return {!Object} * @suppress {unusedLocalVariables} f is only used for nested messages */ proto.net.rantzen.Note.toObject = function(includeInstance, msg) { var f, obj = { id: jspb.Message.getFieldWithDefault(msg, 1, 0), text: jspb.Message.getFieldWithDefault(msg, 2, ""), dates: (f = msg.getDates()) && proto.net.rantzen.Note.Date.toObject(includeInstance, f), type: jspb.Message.getFieldWithDefault(msg, 4, 0) }; if (includeInstance) { obj.$jspbMessageInstance = msg; } return obj; }; } /** * Deserializes binary data (in protobuf wire format). * @param {jspb.ByteSource} bytes The bytes to deserialize. * @return {!proto.net.rantzen.Note} */ proto.net.rantzen.Note.deserializeBinary = function(bytes) { var reader = new jspb.BinaryReader(bytes); var msg = new proto.net.rantzen.Note; return proto.net.rantzen.Note.deserializeBinaryFromReader(msg, reader); }; /** * Deserializes binary data (in protobuf wire format) from the * given reader into the given message object. * @param {!proto.net.rantzen.Note} msg The message object to deserialize into. * @param {!jspb.BinaryReader} reader The BinaryReader to use. * @return {!proto.net.rantzen.Note} */ proto.net.rantzen.Note.deserializeBinaryFromReader = function(msg, reader) { while (reader.nextField()) { if (reader.isEndGroup()) { break; } var field = reader.getFieldNumber(); switch (field) { case 1: var value = /** @type {number} */ (reader.readUint64()); msg.setId(value); break; case 2: var value = /** @type {string} */ (reader.readString()); msg.setText(value); break; case 3: var value = new proto.net.rantzen.Note.Date; reader.readMessage(value,proto.net.rantzen.Note.Date.deserializeBinaryFromReader); msg.setDates(value); break; case 4: var value = /** @type {!proto.net.rantzen.Note.Type} */ (reader.readEnum()); msg.setType(value); break; default: reader.skipField(); break; } } return msg; }; /** * Serializes the message to binary data (in protobuf wire format). * @return {!Uint8Array} */ proto.net.rantzen.Note.prototype.serializeBinary = function() { var writer = new jspb.BinaryWriter(); proto.net.rantzen.Note.serializeBinaryToWriter(this, writer); return writer.getResultBuffer(); }; /** * Serializes the given message to binary data (in protobuf wire * format), writing to the given BinaryWriter. * @param {!proto.net.rantzen.Note} message * @param {!jspb.BinaryWriter} writer * @suppress {unusedLocalVariables} f is only used for nested messages */ proto.net.rantzen.Note.serializeBinaryToWriter = function(message, writer) { var f = undefined; f = message.getId(); if (f !== 0) { writer.writeUint64( 1, f ); } f = message.getText(); if (f.length > 0) { writer.writeString( 2, f ); } f = message.getDates(); if (f != null) { writer.writeMessage( 3, f, proto.net.rantzen.Note.Date.serializeBinaryToWriter ); } f = message.getType(); if (f !== 0.0) { writer.writeEnum( 4, f ); } }; /** * @enum {number} */ proto.net.rantzen.Note.Type = { IDEA: 0, REMINDER: 1 }; /** * Generated by JsPbCodeGenerator. * @param {Array=} opt_data Optional initial data array, typically from a * server response, or constructed directly in Javascript. The array is used * in place and becomes part of the constructed object. It is not cloned. * If no data is provided, the constructed object will be empty, but still * valid. * @extends {jspb.Message} * @constructor */ proto.net.rantzen.Note.Date = function(opt_data) { jspb.Message.initialize(this, opt_data, 0, -1, null, null); }; goog.inherits(proto.net.rantzen.Note.Date, jspb.Message); if (goog.DEBUG && !COMPILED) { proto.net.rantzen.Note.Date.displayName = 'proto.net.rantzen.Note.Date'; } if (jspb.Message.GENERATE_TO_OBJECT) { /** * Creates an object representation of this proto suitable for use in Soy templates. * Field names that are reserved in JavaScript and will be renamed to pb_name. * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. * For the list of reserved names please see: * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. * @param {boolean=} opt_includeInstance Whether to include the JSPB instance * for transitional soy proto support: http://goto/soy-param-migration * @return {!Object} */ proto.net.rantzen.Note.Date.prototype.toObject = function(opt_includeInstance) { return proto.net.rantzen.Note.Date.toObject(opt_includeInstance, this); }; /** * Static version of the {@see toObject} method. * @param {boolean|undefined} includeInstance Whether to include the JSPB * instance for transitional soy proto support: * http://goto/soy-param-migration * @param {!proto.net.rantzen.Note.Date} msg The msg instance to transform. * @return {!Object} * @suppress {unusedLocalVariables} f is only used for nested messages */ proto.net.rantzen.Note.Date.toObject = function(includeInstance, msg) { var f, obj = { created: (f = msg.getCreated()) && proto.google.protobuf.Timestamp.toObject(includeInstance, f), updated: (f = msg.getUpdated()) && proto.google.protobuf.Timestamp.toObject(includeInstance, f) }; if (includeInstance) { obj.$jspbMessageInstance = msg; } return obj; }; } /** * Deserializes binary data (in protobuf wire format). * @param {jspb.ByteSource} bytes The bytes to deserialize. * @return {!proto.net.rantzen.Note.Date} */ proto.net.rantzen.Note.Date.deserializeBinary = function(bytes) { var reader = new jspb.BinaryReader(bytes); var msg = new proto.net.rantzen.Note.Date; return proto.net.rantzen.Note.Date.deserializeBinaryFromReader(msg, reader); }; /** * Deserializes binary data (in protobuf wire format) from the * given reader into the given message object. * @param {!proto.net.rantzen.Note.Date} msg The message object to deserialize into. * @param {!jspb.BinaryReader} reader The BinaryReader to use. * @return {!proto.net.rantzen.Note.Date} */ proto.net.rantzen.Note.Date.deserializeBinaryFromReader = function(msg, reader) { while (reader.nextField()) { if (reader.isEndGroup()) { break; } var field = reader.getFieldNumber(); switch (field) { case 1: var value = new proto.google.protobuf.Timestamp; reader.readMessage(value,proto.google.protobuf.Timestamp.deserializeBinaryFromReader); msg.setCreated(value); break; case 2: var value = new proto.google.protobuf.Timestamp; reader.readMessage(value,proto.google.protobuf.Timestamp.deserializeBinaryFromReader); msg.setUpdated(value); break; default: reader.skipField(); break; } } return msg; }; /** * Serializes the message to binary data (in protobuf wire format). * @return {!Uint8Array} */ proto.net.rantzen.Note.Date.prototype.serializeBinary = function() { var writer = new jspb.BinaryWriter(); proto.net.rantzen.Note.Date.serializeBinaryToWriter(this, writer); return writer.getResultBuffer(); }; /** * Serializes the given message to binary data (in protobuf wire * format), writing to the given BinaryWriter. * @param {!proto.net.rantzen.Note.Date} message * @param {!jspb.BinaryWriter} writer * @suppress {unusedLocalVariables} f is only used for nested messages */ proto.net.rantzen.Note.Date.serializeBinaryToWriter = function(message, writer) { var f = undefined; f = message.getCreated(); if (f != null) { writer.writeMessage( 1, f, proto.google.protobuf.Timestamp.serializeBinaryToWriter ); } f = message.getUpdated(); if (f != null) { writer.writeMessage( 2, f, proto.google.protobuf.Timestamp.serializeBinaryToWriter ); } }; /** * optional google.protobuf.Timestamp created = 1; * @return {?proto.google.protobuf.Timestamp} */ proto.net.rantzen.Note.Date.prototype.getCreated = function() { return /** @type{?proto.google.protobuf.Timestamp} */ ( jspb.Message.getWrapperField(this, proto.google.protobuf.Timestamp, 1)); }; /** @param {?proto.google.protobuf.Timestamp|undefined} value */ proto.net.rantzen.Note.Date.prototype.setCreated = function(value) { jspb.Message.setWrapperField(this, 1, value); }; proto.net.rantzen.Note.Date.prototype.clearCreated = function() { this.setCreated(undefined); }; /** * Returns whether this field is set. * @return {!boolean} */ proto.net.rantzen.Note.Date.prototype.hasCreated = function() { return jspb.Message.getField(this, 1) != null; }; /** * optional google.protobuf.Timestamp updated = 2; * @return {?proto.google.protobuf.Timestamp} */ proto.net.rantzen.Note.Date.prototype.getUpdated = function() { return /** @type{?proto.google.protobuf.Timestamp} */ ( jspb.Message.getWrapperField(this, proto.google.protobuf.Timestamp, 2)); }; /** @param {?proto.google.protobuf.Timestamp|undefined} value */ proto.net.rantzen.Note.Date.prototype.setUpdated = function(value) { jspb.Message.setWrapperField(this, 2, value); }; proto.net.rantzen.Note.Date.prototype.clearUpdated = function() { this.setUpdated(undefined); }; /** * Returns whether this field is set. * @return {!boolean} */ proto.net.rantzen.Note.Date.prototype.hasUpdated = function() { return jspb.Message.getField(this, 2) != null; }; /** * optional uint64 _id = 1; * @return {number} */ proto.net.rantzen.Note.prototype.getId = function() { return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); }; /** @param {number} value */ proto.net.rantzen.Note.prototype.setId = function(value) { jspb.Message.setProto3IntField(this, 1, value); }; /** * optional string text = 2; * @return {string} */ proto.net.rantzen.Note.prototype.getText = function() { return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); }; /** @param {string} value */ proto.net.rantzen.Note.prototype.setText = function(value) { jspb.Message.setProto3StringField(this, 2, value); }; /** * optional Date dates = 3; * @return {?proto.net.rantzen.Note.Date} */ proto.net.rantzen.Note.prototype.getDates = function() { return /** @type{?proto.net.rantzen.Note.Date} */ ( jspb.Message.getWrapperField(this, proto.net.rantzen.Note.Date, 3)); }; /** @param {?proto.net.rantzen.Note.Date|undefined} value */ proto.net.rantzen.Note.prototype.setDates = function(value) { jspb.Message.setWrapperField(this, 3, value); }; proto.net.rantzen.Note.prototype.clearDates = function() { this.setDates(undefined); }; /** * Returns whether this field is set. * @return {!boolean} */ proto.net.rantzen.Note.prototype.hasDates = function() { return jspb.Message.getField(this, 3) != null; }; /** * optional Type type = 4; * @return {!proto.net.rantzen.Note.Type} */ proto.net.rantzen.Note.prototype.getType = function() { return /** @type {!proto.net.rantzen.Note.Type} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); }; /** @param {!proto.net.rantzen.Note.Type} value */ proto.net.rantzen.Note.prototype.setType = function(value) { jspb.Message.setProto3EnumField(this, 4, value); };
/** * @fileoverview * @enhanceable * @suppress {messageConventions} JS Compiler reports an error if a variable or * field starts with 'MSG_' and isn't a translatable message. * @public */ // GENERATED CODE -- DO NOT EDIT! goog.provide('proto.net.rantzen.User'); goog.require('jspb.BinaryReader'); goog.require('jspb.BinaryWriter'); goog.require('jspb.Message'); goog.require('proto.net.rantzen.Note'); /** * Generated by JsPbCodeGenerator. * @param {Array=} opt_data Optional initial data array, typically from a * server response, or constructed directly in Javascript. The array is used * in place and becomes part of the constructed object. It is not cloned. * If no data is provided, the constructed object will be empty, but still * valid. * @extends {jspb.Message} * @constructor */ proto.net.rantzen.User = function(opt_data) { jspb.Message.initialize(this, opt_data, 0, -1, proto.net.rantzen.User.repeatedFields_, null); }; goog.inherits(proto.net.rantzen.User, jspb.Message); if (goog.DEBUG && !COMPILED) { proto.net.rantzen.User.displayName = 'proto.net.rantzen.User'; } /** * List of repeated fields within this message type. * @private {!Array<number>} * @const */ proto.net.rantzen.User.repeatedFields_ = [4]; if (jspb.Message.GENERATE_TO_OBJECT) { /** * Creates an object representation of this proto suitable for use in Soy templates. * Field names that are reserved in JavaScript and will be renamed to pb_name. * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. * For the list of reserved names please see: * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. * @param {boolean=} opt_includeInstance Whether to include the JSPB instance * for transitional soy proto support: http://goto/soy-param-migration * @return {!Object} */ proto.net.rantzen.User.prototype.toObject = function(opt_includeInstance) { return proto.net.rantzen.User.toObject(opt_includeInstance, this); }; /** * Static version of the {@see toObject} method. * @param {boolean|undefined} includeInstance Whether to include the JSPB * instance for transitional soy proto support: * http://goto/soy-param-migration * @param {!proto.net.rantzen.User} msg The msg instance to transform. * @return {!Object} * @suppress {unusedLocalVariables} f is only used for nested messages */ proto.net.rantzen.User.toObject = function(includeInstance, msg) { var f, obj = { id: jspb.Message.getFieldWithDefault(msg, 1, 0), email: jspb.Message.getFieldWithDefault(msg, 2, ""), password: jspb.Message.getFieldWithDefault(msg, 3, ""), notesList: jspb.Message.toObjectList(msg.getNotesList(), proto.net.rantzen.Note.toObject, includeInstance) }; if (includeInstance) { obj.$jspbMessageInstance = msg; } return obj; }; } /** * Deserializes binary data (in protobuf wire format). * @param {jspb.ByteSource} bytes The bytes to deserialize. * @return {!proto.net.rantzen.User} */ proto.net.rantzen.User.deserializeBinary = function(bytes) { var reader = new jspb.BinaryReader(bytes); var msg = new proto.net.rantzen.User; return proto.net.rantzen.User.deserializeBinaryFromReader(msg, reader); }; /** * Deserializes binary data (in protobuf wire format) from the * given reader into the given message object. * @param {!proto.net.rantzen.User} msg The message object to deserialize into. * @param {!jspb.BinaryReader} reader The BinaryReader to use. * @return {!proto.net.rantzen.User} */ proto.net.rantzen.User.deserializeBinaryFromReader = function(msg, reader) { while (reader.nextField()) { if (reader.isEndGroup()) { break; } var field = reader.getFieldNumber(); switch (field) { case 1: var value = /** @type {number} */ (reader.readUint64()); msg.setId(value); break; case 2: var value = /** @type {string} */ (reader.readString()); msg.setEmail(value); break; case 3: var value = /** @type {string} */ (reader.readString()); msg.setPassword(value); break; case 4: var value = new proto.net.rantzen.Note; reader.readMessage(value,proto.net.rantzen.Note.deserializeBinaryFromReader); msg.addNotes(value); break; default: reader.skipField(); break; } } return msg; }; /** * Serializes the message to binary data (in protobuf wire format). * @return {!Uint8Array} */ proto.net.rantzen.User.prototype.serializeBinary = function() { var writer = new jspb.BinaryWriter(); proto.net.rantzen.User.serializeBinaryToWriter(this, writer); return writer.getResultBuffer(); }; /** * Serializes the given message to binary data (in protobuf wire * format), writing to the given BinaryWriter. * @param {!proto.net.rantzen.User} message * @param {!jspb.BinaryWriter} writer * @suppress {unusedLocalVariables} f is only used for nested messages */ proto.net.rantzen.User.serializeBinaryToWriter = function(message, writer) { var f = undefined; f = message.getId(); if (f !== 0) { writer.writeUint64( 1, f ); } f = message.getEmail(); if (f.length > 0) { writer.writeString( 2, f ); } f = message.getPassword(); if (f.length > 0) { writer.writeString( 3, f ); } f = message.getNotesList(); if (f.length > 0) { writer.writeRepeatedMessage( 4, f, proto.net.rantzen.Note.serializeBinaryToWriter ); } }; /** * optional uint64 _id = 1; * @return {number} */ proto.net.rantzen.User.prototype.getId = function() { return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); }; /** @param {number} value */ proto.net.rantzen.User.prototype.setId = function(value) { jspb.Message.setProto3IntField(this, 1, value); }; /** * optional string email = 2; * @return {string} */ proto.net.rantzen.User.prototype.getEmail = function() { return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); }; /** @param {string} value */ proto.net.rantzen.User.prototype.setEmail = function(value) { jspb.Message.setProto3StringField(this, 2, value); }; /** * optional string password = 3; * @return {string} */ proto.net.rantzen.User.prototype.getPassword = function() { return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); }; /** @param {string} value */ proto.net.rantzen.User.prototype.setPassword = function(value) { jspb.Message.setProto3StringField(this, 3, value); }; /** * repeated Note notes = 4; * @return {!Array<!proto.net.rantzen.Note>} */ proto.net.rantzen.User.prototype.getNotesList = function() { return /** @type{!Array<!proto.net.rantzen.Note>} */ ( jspb.Message.getRepeatedWrapperField(this, proto.net.rantzen.Note, 4)); }; /** @param {!Array<!proto.net.rantzen.Note>} value */ proto.net.rantzen.User.prototype.setNotesList = function(value) { jspb.Message.setRepeatedWrapperField(this, 4, value); }; /** * @param {!proto.net.rantzen.Note=} opt_value * @param {number=} opt_index * @return {!proto.net.rantzen.Note} */ proto.net.rantzen.User.prototype.addNotes = function(opt_value, opt_index) { return jspb.Message.addToRepeatedWrapperField(this, 4, opt_value, proto.net.rantzen.Note, opt_index); }; proto.net.rantzen.User.prototype.clearNotesList = function() { this.setNotesList([]); };
[/su_spoiler]
Benutzung
Folgt in kürze…
Zusammengefasst sind Protocol Buffers also manuel geschriebene Stubs einer theoretischen Klasse zur kommunikation zwischen Systemen.
Wenn es euch geholfen hat, oder ihr konstruktive Kritik habt, schreibt mir bitte ein Kommentar :).
0 Kommentare