/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @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');
goog.requireType('goog.proto2.Descriptor');
/**
* 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.proto2.Serializer.DECODE_SYMBOLIC_ENUMS =
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) {
'use strict';
if (field.isCompositeType()) {
return this.serialize(/** @type {goog.proto2.Message} */ (value));
} else if (typeof value === 'number' && !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) {
'use strict';
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) {
'use strict';
// 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 &&
typeof value === 'string') {
// 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 (typeof value === 'string' &&
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 (typeof value === 'number') {
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 (typeof value === 'string') {
// 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]+$/;