blob: 2a9260c9f96ad5bb6f09d45a1971d49efb526e70 [file] [log] [blame]
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Protocol Buffer 2 Serializer which serializes messages
* into PB-Lite ("JsPbLite") format.
*
* PB-Lite format is an array where each index corresponds to the associated tag
* number. For example, a message like so:
*
* message Foo {
* optional int bar = 1;
* optional int baz = 2;
* optional int bop = 4;
* }
*
* would be represented as such:
*
* [, (bar data), (baz data), (nothing), (bop data)]
*
* Note that since the array index is used to represent the tag number, sparsely
* populated messages with tag numbers that are not continuous (and/or are very
* large) will have many (empty) spots and thus, are inefficient.
*
*
*/
goog.provide('goog.proto2.PbLiteSerializer');
goog.require('goog.asserts');
goog.require('goog.proto2.FieldDescriptor');
goog.require('goog.proto2.LazyDeserializer');
goog.require('goog.proto2.Serializer');
/**
* PB-Lite serializer.
*
* @constructor
* @extends {goog.proto2.LazyDeserializer}
*/
goog.proto2.PbLiteSerializer = function() {};
goog.inherits(goog.proto2.PbLiteSerializer, goog.proto2.LazyDeserializer);
/**
* If true, fields will be serialized with 0-indexed tags (i.e., the proto
* field with tag id 1 will have index 0 in the array).
* @type {boolean}
* @private
*/
goog.proto2.PbLiteSerializer.prototype.zeroIndexing_ = false;
/**
* By default, the proto tag with id 1 will have index 1 in the serialized
* array.
*
* If the serializer is set to use zero-indexing, the tag with id 1 will have
* index 0.
*
* @param {boolean} zeroIndexing Whether this serializer should deal with
* 0-indexed protos.
*/
goog.proto2.PbLiteSerializer.prototype.setZeroIndexed = function(zeroIndexing) {
this.zeroIndexing_ = zeroIndexing;
};
/**
* Serializes a message to a PB-Lite object.
*
* @param {goog.proto2.Message} message The message to be serialized.
* @return {!Array<?>} The serialized form of the message.
* @override
*/
goog.proto2.PbLiteSerializer.prototype.serialize = function(message) {
var descriptor = message.getDescriptor();
var fields = descriptor.getFields();
var serialized = [];
var zeroIndexing = this.zeroIndexing_;
// Add the known fields.
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
if (!message.has(field)) {
continue;
}
var tag = field.getTag();
var index = zeroIndexing ? tag - 1 : tag;
if (field.isRepeated()) {
serialized[index] = [];
for (var j = 0; j < message.countOf(field); j++) {
serialized[index][j] =
this.getSerializedValue(field, message.get(field, j));
}
} else {
serialized[index] = this.getSerializedValue(field, message.get(field));
}
}
// Add any unknown fields.
message.forEachUnknown(function(tag, value) {
var index = zeroIndexing ? tag - 1 : tag;
serialized[index] = value;
});
return serialized;
};
/** @override */
goog.proto2.PbLiteSerializer.prototype.deserializeField =
function(message, field, value) {
if (value == null) {
// Since value double-equals null, it may be either null or undefined.
// Ensure we return the same one, since they have different meanings.
// TODO(user): If the field is repeated, this method should probably
// return [] instead of null.
return value;
}
if (field.isRepeated()) {
var data = [];
goog.asserts.assert(goog.isArray(value), 'Value must be array: %s', value);
for (var i = 0; i < value.length; i++) {
data[i] = this.getDeserializedValue(field, value[i]);
}
return data;
} else {
return this.getDeserializedValue(field, value);
}
};
/** @override */
goog.proto2.PbLiteSerializer.prototype.getSerializedValue =
function(field, value) {
if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.BOOL) {
// Booleans are serialized in numeric form.
return value ? 1 : 0;
}
return goog.proto2.Serializer.prototype.getSerializedValue.apply(this,
arguments);
};
/** @override */
goog.proto2.PbLiteSerializer.prototype.getDeserializedValue =
function(field, value) {
if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.BOOL) {
goog.asserts.assert(goog.isNumber(value) || goog.isBoolean(value),
'Value is expected to be a number or boolean');
return !!value;
}
return goog.proto2.Serializer.prototype.getDeserializedValue.apply(this,
arguments);
};
/** @override */
goog.proto2.PbLiteSerializer.prototype.deserialize =
function(descriptor, data) {
var toConvert = data;
if (this.zeroIndexing_) {
// Make the data align with tag-IDs (1-indexed) by shifting everything
// up one.
toConvert = [];
for (var key in data) {
toConvert[parseInt(key, 10) + 1] = data[key];
}
}
return goog.proto2.PbLiteSerializer.base(
this, 'deserialize', descriptor, toConvert);
};