// 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 Base class for all Protocol Buffer 2 serializers. */ goog.provide('goog.proto2.Serializer'); goog.require('goog.asserts'); goog.require('goog.proto2.FieldDescriptor'); goog.require('goog.proto2.Message'); /** * Abstract base class for PB2 serializers. A serializer is a class which * implements the serialization and deserialization of a Protocol Buffer Message * to/from a specific format. * * @constructor */ goog.proto2.Serializer = function() {}; /** * @define {boolean} Whether to decode and convert symbolic enum values to * actual enum values or leave them as strings. */ goog.define('goog.proto2.Serializer.DECODE_SYMBOLIC_ENUMS', false); /** * Serializes a message to the expected format. * * @param {goog.proto2.Message} message The message to be serialized. * * @return {*} The serialized form of the message. */ goog.proto2.Serializer.prototype.serialize = goog.abstractMethod; /** * Returns the serialized form of the given value for the given field if the * field is a Message or Group and returns the value unchanged otherwise, except * for Infinity, -Infinity and NaN numerical values which are converted to * string representation. * * @param {goog.proto2.FieldDescriptor} field The field from which this * value came. * * @param {*} value The value of the field. * * @return {*} The value. * @protected */ goog.proto2.Serializer.prototype.getSerializedValue = function(field, value) { if (field.isCompositeType()) { return this.serialize(/** @type {goog.proto2.Message} */ (value)); } else if (goog.isNumber(value) && !isFinite(value)) { return value.toString(); } else { return value; } }; /** * Deserializes a message from the expected format. * * @param {goog.proto2.Descriptor} descriptor The descriptor of the message * to be created. * @param {*} data The data of the message. * * @return {!goog.proto2.Message} The message created. */ goog.proto2.Serializer.prototype.deserialize = function(descriptor, data) { var message = descriptor.createMessageInstance(); this.deserializeTo(message, data); goog.asserts.assert(message instanceof goog.proto2.Message); return message; }; /** * Deserializes a message from the expected format and places the * data in the message. * * @param {goog.proto2.Message} message The message in which to * place the information. * @param {*} data The data of the message. */ goog.proto2.Serializer.prototype.deserializeTo = goog.abstractMethod; /** * Returns the deserialized form of the given value for the given field if the * field is a Message or Group and returns the value, converted or unchanged, * for primitive field types otherwise. * * @param {goog.proto2.FieldDescriptor} field The field from which this * value came. * * @param {*} value The value of the field. * * @return {*} The value. * @protected */ goog.proto2.Serializer.prototype.getDeserializedValue = function(field, value) { // Composite types are deserialized recursively. if (field.isCompositeType()) { if (value instanceof goog.proto2.Message) { return value; } return this.deserialize(field.getFieldMessageType(), value); } // Decode enum values. if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.ENUM) { // If it's a string, get enum value by name. // NB: In order this feature to work, property renaming should be turned off // for the respective enums. if (goog.proto2.Serializer.DECODE_SYMBOLIC_ENUMS && goog.isString(value)) { // enumType is a regular Javascript enum as defined in field's metadata. var enumType = field.getNativeType(); if (enumType.hasOwnProperty(value)) { return enumType[value]; } } // If it's a string containing a positive integer, this looks like a viable // enum int value. Return as numeric. if (goog.isString(value) && goog.proto2.Serializer.INTEGER_REGEX.test(value)) { var numeric = Number(value); if (numeric > 0) { return numeric; } } // Return unknown values as is for backward compatibility. return value; } // Return the raw value if the field does not allow the JSON input to be // converted. if (!field.deserializationConversionPermitted()) { return value; } // Convert to native type of field. Return the converted value or fall // through to return the raw value. The JSON encoding of int64 value 123 // might be either the number 123 or the string "123". The field native type // could be either Number or String (depending on field options in the .proto // file). All four combinations should work correctly. var nativeType = field.getNativeType(); if (nativeType === String) { // JSON numbers can be converted to strings. if (goog.isNumber(value)) { return String(value); } } else if (nativeType === Number) { // JSON strings are sometimes used for large integer numeric values, as well // as Infinity, -Infinity and NaN. if (goog.isString(value)) { // Handle +/- Infinity and NaN values. if (value === 'Infinity' || value === '-Infinity' || value === 'NaN') { return Number(value); } // Validate the string. If the string is not an integral number, we would // rather have an assertion or error in the caller than a mysterious NaN // value. if (goog.proto2.Serializer.INTEGER_REGEX.test(value)) { return Number(value); } } } return value; }; /** @const {!RegExp} */ goog.proto2.Serializer.INTEGER_REGEX = /^-?[0-9]+$/;