1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
# frozen_string_literal: true
require 'base64'
module Aws
module Xml
class Builder
include Seahorse::Model::Shapes
def initialize(rules, options = {})
@rules = rules
@xml = options[:target] || []
indent = options[:indent] || ' '
pad = options[:pad] || ''
@builder = DocBuilder.new(target: @xml, indent: indent, pad: pad)
end
def to_xml(params)
structure(@rules.location_name, @rules, params)
@xml.join
end
alias serialize to_xml
private
def structure(name, ref, values)
if values.empty?
node(name, ref)
else
node(name, ref, structure_attrs(ref, values)) do
ref.shape.members.each do |member_name, member_ref|
next if values[member_name].nil?
next if xml_attribute?(member_ref)
member(member_ref.location_name, member_ref, values[member_name])
end
end
end
end
def structure_attrs(ref, values)
ref.shape.members.inject({}) do |attrs, (member_name, member_ref)|
if xml_attribute?(member_ref) && values.key?(member_name)
attrs[member_ref.location_name] = values[member_name]
end
attrs
end
end
def list(name, ref, values)
if ref.shape.flattened
values.each do |value|
member(ref.shape.member.location_name || name, ref.shape.member, value)
end
else
node(name, ref) do
values.each do |value|
mname = ref.shape.member.location_name || 'member'
member(mname, ref.shape.member, value)
end
end
end
end
def map(name, ref, hash)
key_ref = ref.shape.key
value_ref = ref.shape.value
if ref.shape.flattened
hash.each do |key, value|
node(name, ref) do
member(key_ref.location_name || 'key', key_ref, key)
member(value_ref.location_name || 'value', value_ref, value)
end
end
else
node(name, ref) do
hash.each do |key, value|
node('entry', ref) do
member(key_ref.location_name || 'key', key_ref, key)
member(value_ref.location_name || 'value', value_ref, value)
end
end
end
end
end
def member(name, ref, value)
case ref.shape
when StructureShape then structure(name, ref, value)
when ListShape then list(name, ref, value)
when MapShape then map(name, ref, value)
when TimestampShape then node(name, ref, timestamp(ref, value))
when BlobShape then node(name, ref, blob(value))
else
node(name, ref, value.to_s)
end
end
def blob(value)
value = value.read unless String === value
Base64.strict_encode64(value)
end
def timestamp(ref, value)
case ref['timestampFormat'] || ref.shape['timestampFormat']
when 'unixTimestamp' then value.to_i
when 'rfc822' then value.utc.httpdate
else
# xml defaults to iso8601
value.utc.iso8601
end
end
# The `args` list may contain:
#
# * [] - empty, no value or attributes
# * [value] - inline element, no attributes
# * [value, attributes_hash] - inline element with attributes
# * [attributes_hash] - self closing element with attributes
#
# Pass a block if you want to nest XML nodes inside. When doing this,
# you may *not* pass a value to the `args` list.
#
def node(name, ref, *args, &block)
attrs = args.last.is_a?(Hash) ? args.pop : {}
attrs = shape_attrs(ref).merge(attrs)
args << attrs
@builder.node(name, *args, &block)
end
def shape_attrs(ref)
if xmlns = ref['xmlNamespace']
if prefix = xmlns['prefix']
{ 'xmlns:' + prefix => xmlns['uri'] }
else
{ 'xmlns' => xmlns['uri'] }
end
else
{}
end
end
def xml_attribute?(ref)
!!ref['xmlAttribute']
end
end
end
end
|