class MsRest::Serialization::Serialization
Class to handle serialization & deserialization.
Public Class Methods
# File lib/ms_rest/serialization.rb, line 46 def initialize(context) @context = context end
Public Instance Methods
Deserialize the response from the server using the mapper.
@param mapper [Hash] Ruby Hash object to represent expected structure of the response_body. @param response_body [Hash] Ruby Hash object to deserialize. @param object_name [String] Name of the deserialized object.
# File lib/ms_rest/serialization.rb, line 57 def deserialize(mapper, response_body) return response_body if response_body.nil? object_name = mapper[:serialized_name] mapper_type = mapper[:type][:name] if !mapper_type.match(/^(Number|Double|ByteArray|Boolean|Date|DateTime|DateTimeRfc1123|TimeSpan|UnixTime|Enum|String|Object|Stream)$/i).nil? payload = deserialize_primary_type(mapper, response_body) elsif !mapper_type.match(/^Dictionary$/i).nil? payload = deserialize_dictionary_type(mapper, response_body, object_name) elsif !mapper_type.match(/^Composite$/i).nil? payload = deserialize_composite_type(mapper, response_body, object_name) elsif !mapper_type.match(/^Sequence$/i).nil? payload = deserialize_sequence_type(mapper, response_body, object_name) else payload = "" end payload = mapper[:default_value] if mapper[:is_constant] payload end
Deserialize the response of composite type from the server using the mapper.
@param mapper [Hash] Ruby Hash object to represent expected structure of the response_body. @param response_body [Hash] Ruby Hash object to deserialize. @param object_name [String] Name of the deserialized object.
# File lib/ms_rest/serialization.rb, line 146 def deserialize_composite_type(mapper, response_body, object_name) if mapper[:type][:class_name].nil? fail DeserializationError.new("'class_name' metadata for a composite type must be defined in the mapper and it must be of type Hash in #{object_name}", nil, nil, response_body) end if !mapper[:type][:polymorphic_discriminator].nil? # Handle polymorphic types parent_class = get_model(mapper[:type][:class_name]) discriminator = parent_class.class_eval("@@discriminatorMap") model_name = response_body["#{mapper[:type][:polymorphic_discriminator]}"] # In case we do not find model from response body then use the class defined in mapper model_name = mapper[:type][:class_name] if model_name.nil? || model_name.empty? model_class = get_model(discriminator[model_name]) else model_class = get_model(mapper[:type][:class_name]) end result = model_class.new model_mapper = model_class.mapper() model_props = model_mapper[:type][:model_properties] unless model_props.nil? model_props.each do |key, val| sub_response_body = nil unless val[:serialized_name].to_s.include? '.' sub_response_body = response_body[val[:serialized_name].to_s] else # Flattened properties will be dicovered at deeper level in payload but must be deserialized to higher levels in model class sub_response_body = response_body levels = split_serialized_name(val[:serialized_name].to_s) levels.each { |level| sub_response_body = sub_response_body.nil? ? nil : sub_response_body[level.to_s] } end result.instance_variable_set("@#{key}", deserialize(val, sub_response_body)) unless sub_response_body.nil? end end result end
Deserialize the response of dictionary type from the server using the mapper.
@param mapper [Hash] Ruby Hash object to represent expected structure of the response_body. @param response_body [Hash] Ruby Hash object to deserialize. @param object_name [String] Name of the deserialized object.
# File lib/ms_rest/serialization.rb, line 127 def deserialize_dictionary_type(mapper, response_body, object_name) if mapper[:type][:value].nil? || !mapper[:type][:value].is_a?(Hash) fail DeserializationError.new("'value' metadata for a dictionary type must be defined in the mapper and it must be of type Hash in #{object_name}", nil, nil, response_body) end result = Hash.new response_body.each do |key, val| result[key] = deserialize(mapper[:type][:value], val) end result end
Deserialize the response of known primary type from the server using the mapper.
@param mapper [Hash] Ruby Hash object to represent expected structure of the response_body. @param response_body [Hash] Ruby Hash object to deserialize.
# File lib/ms_rest/serialization.rb, line 86 def deserialize_primary_type(mapper, response_body) result = "" case mapper[:type][:name] when 'Number' result = Integer(response_body) unless response_body.to_s.empty? when 'Double' result = Float(response_body) unless response_body.to_s.empty? when 'ByteArray' result = Base64.strict_decode64(response_body).unpack('C*') unless response_body.to_s.empty? when 'String', 'Boolean', 'Object', 'Stream', 'TimeSpan' result = response_body when 'Enum' unless response_body.nil? unless enum_is_valid(mapper, response_body) warn "Enum does not contain #{response_body}, but was received from the server." end end result = response_body when 'Date' unless response_body.to_s.empty? result = Timeliness.parse(response_body, :strict => true) fail DeserializationError.new('Error occured in deserializing the response_body', nil, nil, response_body) if result.nil? result = ::Date.parse(result.to_s) end when 'DateTime', 'DateTimeRfc1123' result = DateTime.parse(response_body) unless response_body.to_s.empty? when 'UnixTime' result = DateTime.strptime(response_body.to_s, '%s') unless response_body.to_s.empty? else result end result end
Deserialize the response of sequence type from the server using the mapper.
@param mapper [Hash] Ruby Hash object to represent expected structure of the response_body. @param response_body [Hash] Ruby Hash object to deserialize. @param object_name [String] Name of the deserialized object.
# File lib/ms_rest/serialization.rb, line 193 def deserialize_sequence_type(mapper, response_body, object_name) if mapper[:type][:element].nil? || !mapper[:type][:element].is_a?(Hash) fail DeserializationError.new("'element' metadata for a sequence type must be defined in the mapper and it must be of type Hash in #{object_name}", nil, nil, response_body) end return response_body if response_body.nil? result = [] response_body.each do |element| result.push(deserialize(mapper[:type][:element], element)) end result end
Checks whether given enum_value is valid for the mapper or not
@param mapper [Hash] Ruby Hash object containing meta data @param enum_value [String] Enum value to validate
# File lib/ms_rest/serialization.rb, line 451 def enum_is_valid(mapper, enum_value) if enum_value.is_a?(String) && !enum_value.empty? model = get_model(mapper[:type][:module]) model.constants.any? { |e| model.const_get(e).to_s.downcase == enum_value.downcase } else false end end
Retrieves model of the model_name
@param model_name [String] Name of the model to retrieve.
# File lib/ms_rest/serialization.rb, line 428 def get_model(model_name) begin consts = @context.class.to_s.split('::') end_index = 0 if consts.any?{ |const| const == 'Models' } # context is a model class end_index = -2 else # context is a service, so find the model class end_index = -1 end Object.const_get(consts[0...end_index].join('::') + "::Models::#{model_name}") rescue Object.const_get("MsRestAzure::#{model_name}") end end
Serialize the Ruby object into Ruby Hash to send it to the server using the mapper.
@param mapper [Hash] Ruby Hash object to represent expected structure of the object. @param object [Object] Ruby object to serialize. @param object_name [String] Name of the serialized object.
# File lib/ms_rest/serialization.rb, line 215 def serialize(mapper, object) object_name = mapper[:serialized_name] # Set defaults unless mapper[:default_value].nil? object = mapper[:default_value] if object.nil? end object = mapper[:default_value] if mapper[:is_constant] validate_constraints(mapper, object, object_name) if !mapper[:required] && object.nil? return object end payload = Hash.new mapper_type = mapper[:type][:name] if !mapper_type.match(/^(Number|Double|ByteArray|Boolean|Date|DateTime|DateTimeRfc1123|TimeSpan|UnixTime|Enum|String|Object|Stream)$/i).nil? payload = serialize_primary_type(mapper, object) elsif !mapper_type.match(/^Dictionary$/i).nil? payload = serialize_dictionary_type(mapper, object, object_name) elsif !mapper_type.match(/^Composite$/i).nil? payload = serialize_composite_type(mapper, object, object_name) elsif !mapper_type.match(/^Sequence$/i).nil? payload = serialize_sequence_type(mapper, object, object_name) end payload end
Serialize the Ruby object of composite type into Ruby Hash to send it to the server using the mapper.
@param mapper [Hash] Ruby Hash object to represent expected structure of the object. @param object [Object] Ruby object to serialize. @param object_name [String] Name of the serialized object.
# File lib/ms_rest/serialization.rb, line 347 def serialize_composite_type(mapper, object, object_name) if !mapper[:type][:polymorphic_discriminator].nil? # Handle polymorphic types model_name = object.class.to_s.split('::')[-1] model_class = get_model(model_name) else model_class = get_model(mapper[:type][:class_name]) end payload = Hash.new model_mapper = model_class.mapper() model_props = model_mapper[:type][:model_properties] unless model_props.nil? model_props.each do |key, value| begin instance_variable = object.instance_variable_get("@#{key}") rescue NameError warn("Instance variable '#{key}' is expected on '#{object.class}'.") end if !instance_variable.nil? && instance_variable.respond_to?(:validate) instance_variable.validate end # Read only properties should not be sent on wire if !model_props[key][:read_only].nil? && model_props[key][:read_only] next end sub_payload = serialize(value, instance_variable) unless value[:serialized_name].to_s.include? '.' payload[value[:serialized_name].to_s] = sub_payload unless sub_payload.nil? else # Flattened properties will be discovered at higher levels in model class but must be serialized to deeper level in payload levels = split_serialized_name(value[:serialized_name].to_s) last_level = levels.pop temp_payload = payload levels.each do |level| temp_payload[level] = Hash.new unless temp_payload.key?(level) temp_payload = temp_payload[level] end temp_payload[last_level] = sub_payload unless sub_payload.nil? end end end payload end
Serialize the Ruby object of dictionary type into Ruby Hash to send it to the server using the mapper.
@param mapper [Hash] Ruby Hash object to represent expected structure of the object. @param object [Object] Ruby object to serialize. @param object_name [String] Name of the serialized object.
# File lib/ms_rest/serialization.rb, line 320 def serialize_dictionary_type(mapper, object, object_name) unless object.is_a?(Hash) fail DeserializationError.new("#{object_name} must be of type Hash", nil, nil, object) end unless mapper[:type][:value].nil? || mapper[:type][:value].is_a?(Hash) fail DeserializationError.new("'value' metadata for a dictionary type must be defined in the mapper and it must be of type Hash in #{object_name}", nil, nil, object) end payload = Hash.new object.each do |key, value| if !value.nil? && value.respond_to?(:validate) value.validate end payload[key] = serialize(mapper[:type][:value], value) end payload end
Serialize the Ruby object of known primary type into Ruby Hash to send it to the server using the mapper.
@param mapper [Hash] Ruby Hash object to represent expected structure of the object. @param object [Object] Ruby object to serialize.
# File lib/ms_rest/serialization.rb, line 288 def serialize_primary_type(mapper, object) mapper_type = mapper[:type][:name] payload = nil case mapper_type when 'Number', 'Double', 'String', 'Date', 'TimeSpan', 'Boolean', 'Object', 'Stream' payload = object != nil ? object : nil when 'Enum' unless object.nil? unless enum_is_valid(mapper, object) fail ValidationError, "Enum #{mapper[:type][:module]} does not contain #{object.to_s}, but trying to send it to the server." end end payload = object != nil ? object : nil when 'ByteArray' payload = Base64.strict_encode64(object.pack('c*')) when 'DateTime' payload = object.new_offset(0).strftime('%FT%TZ') when 'DateTimeRfc1123' payload = object.new_offset(0).strftime('%a, %d %b %Y %H:%M:%S GMT') when 'UnixTime' payload = object.new_offset(0).strftime('%s') unless object.nil? end payload end
Serialize the Ruby object of sequence type into Ruby Hash to send it to the server using the mapper.
@param mapper [Hash] Ruby Hash object to represent expected structure of the object. @param object [Object] Ruby object to serialize. @param object_name [String] Name of the serialized object.
# File lib/ms_rest/serialization.rb, line 404 def serialize_sequence_type(mapper, object, object_name) unless object.is_a?(Array) fail DeserializationError.new("#{object_name} must be of type of Array", nil, nil, object) end unless mapper[:type][:element].nil? || mapper[:type][:element].is_a?(Hash) fail DeserializationError.new("'element' metadata for a sequence type must be defined in the mapper and it must be of type Hash in #{object_name}", nil, nil, object) end payload = Array.new object.each do |element| if !element.nil? && element.respond_to?(:validate) element.validate end payload.push(serialize(mapper[:type][:element], element)) end payload end
Splits serialized_name with '.' to compute levels of object hierarchy
@param serialized_name [String] Name to split
# File lib/ms_rest/serialization.rb, line 464 def split_serialized_name(serialized_name) result = Array.new element = '' levels = serialized_name.to_s.split('.') levels.each do |level| unless level.match(/.*\\$/).nil? # Flattened properties will be discovered at different levels in model class and response body element = "#{element}#{level.gsub!('\\','')}." else element = "#{element}#{level}" result.push(element) unless element.empty? element = '' end end result end
# File lib/ms_rest/serialization.rb, line 244 def validate_constraints(mapper, object, object_name) if(mapper[:client_side_validation]) # Throw if required & non-constant object is nil if mapper[:required] && object.nil? && !mapper[:is_constant] fail ValidationError, "#{object_name} is required and cannot be nil" end if(mapper[:constraints]) mapper[:constraints].each do |constraint_name, constraint_value| case constraint_name.to_s.downcase when 'exclusivemaximum' fail ValidationError, "#{object_name} with value '#{object}' should satisfy the constraint 'ExclusiveMaximum': '#{constraint_value}'" if !object.nil? && object >= constraint_value when 'exclusiveminimum' fail ValidationError, "#{object_name} with value '#{object}' should satisfy the constraint 'ExclusiveMinimum': '#{constraint_value}'" if !object.nil? && object <= constraint_value when 'inclusivemaximum' fail ValidationError, "#{object_name} with value '#{object}' should satisfy the constraint 'InclusiveMaximum': '#{constraint_value}'" if !object.nil? && object > constraint_value when 'inclusiveminimum' fail ValidationError, "#{object_name} with value '#{object}' should satisfy the constraint 'InclusiveMinimum': '#{constraint_value}'" if !object.nil? && object < constraint_value when 'maxitems' fail ValidationError, "#{object_name} with value '#{object}' should satisfy the constraint 'MaxItems': '#{constraint_value}'" if !object.nil? && object.length > constraint_value when 'minitems' fail ValidationError, "#{object_name} with value '#{object}' should satisfy the constraint 'MinItems': '#{constraint_value}'" if !object.nil? && object.length < constraint_value when 'maxlength' fail ValidationError, "#{object_name} with value '#{object}' should satisfy the constraint 'MaxLength': '#{constraint_value}'" if !object.nil? && object.length > constraint_value when 'minlength' fail ValidationError, "#{object_name} with value '#{object}' should satisfy the constraint 'MinLength': '#{constraint_value}'" if !object.nil? && object.length < constraint_value when 'multipleof' fail ValidationError, "#{object_name} with value '#{object}' should satisfy the constraint 'MultipleOf': '#{constraint_value}'" if !object.nil? && object % constraint_value != 0 when 'pattern' fail ValidationError, "#{object_name} with value '#{object}' should satisfy the constraint 'Pattern': '#{constraint_value}'" if !object.nil? && object.match(Regexp.new "^#{constraint_value}$").nil? when 'uniqueitems' fail ValidationError, "#{object_name} with value '#{object}' should satisfy the constraint 'UniqueItems': '#{constraint_value}'" if !object.nil? && object.length != object.uniq.length end end end end end