13 "github.com/aws/aws-sdk-go/private/protocol"
16 // Parse parses an object i and fills a url.Values object. The isEC2 flag
17 // indicates if this is the EC2 Query sub-protocol.
18 func Parse(body url.Values, i interface{}, isEC2 bool) error {
19 q := queryParser{isEC2: isEC2}
20 return q.parseValue(body, reflect.ValueOf(i), "", "")
23 func elemOf(value reflect.Value) reflect.Value {
24 for value.Kind() == reflect.Ptr {
30 type queryParser struct {
34 func (q *queryParser) parseValue(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
37 // no need to handle zero values
56 return q.parseStruct(v, value, prefix)
58 return q.parseList(v, value, prefix, tag)
60 return q.parseMap(v, value, prefix, tag)
62 return q.parseScalar(v, value, prefix, tag)
66 func (q *queryParser) parseStruct(v url.Values, value reflect.Value, prefix string) error {
72 for i := 0; i < value.NumField(); i++ {
73 elemValue := elemOf(value.Field(i))
76 if field.PkgPath != "" {
77 continue // ignore unexported fields
79 if field.Tag.Get("ignore") != "" {
83 if protocol.CanSetIdempotencyToken(value.Field(i), field) {
84 token := protocol.GetIdempotencyToken()
85 elemValue = reflect.ValueOf(token)
90 name = field.Tag.Get("queryName")
93 if field.Tag.Get("flattened") != "" && field.Tag.Get("locationNameList") != "" {
94 name = field.Tag.Get("locationNameList")
95 } else if locName := field.Tag.Get("locationName"); locName != "" {
98 if name != "" && q.isEC2 {
99 name = strings.ToUpper(name[0:1]) + name[1:]
107 name = prefix + "." + name
110 if err := q.parseValue(v, elemValue, name, field.Tag); err != nil {
117 func (q *queryParser) parseList(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
118 // If it's empty, generate an empty value
119 if !value.IsNil() && value.Len() == 0 {
124 if _, ok := value.Interface().([]byte); ok {
125 return q.parseScalar(v, value, prefix, tag)
128 // check for unflattened list member
129 if !q.isEC2 && tag.Get("flattened") == "" {
130 if listName := tag.Get("locationNameList"); listName == "" {
133 prefix += "." + listName
137 for i := 0; i < value.Len(); i++ {
138 slicePrefix := prefix
139 if slicePrefix == "" {
140 slicePrefix = strconv.Itoa(i + 1)
142 slicePrefix = slicePrefix + "." + strconv.Itoa(i+1)
144 if err := q.parseValue(v, value.Index(i), slicePrefix, ""); err != nil {
151 func (q *queryParser) parseMap(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
152 // If it's empty, generate an empty value
153 if !value.IsNil() && value.Len() == 0 {
158 // check for unflattened list member
159 if !q.isEC2 && tag.Get("flattened") == "" {
163 // sort keys for improved serialization consistency.
164 // this is not strictly necessary for protocol support.
165 mapKeyValues := value.MapKeys()
166 mapKeys := map[string]reflect.Value{}
167 mapKeyNames := make([]string, len(mapKeyValues))
168 for i, mapKey := range mapKeyValues {
169 name := mapKey.String()
170 mapKeys[name] = mapKey
171 mapKeyNames[i] = name
173 sort.Strings(mapKeyNames)
175 for i, mapKeyName := range mapKeyNames {
176 mapKey := mapKeys[mapKeyName]
177 mapValue := value.MapIndex(mapKey)
179 kname := tag.Get("locationNameKey")
183 vname := tag.Get("locationNameValue")
191 keyName = strconv.Itoa(i+1) + "." + kname
193 keyName = prefix + "." + strconv.Itoa(i+1) + "." + kname
196 if err := q.parseValue(v, mapKey, keyName, ""); err != nil {
203 valueName = strconv.Itoa(i+1) + "." + vname
205 valueName = prefix + "." + strconv.Itoa(i+1) + "." + vname
208 if err := q.parseValue(v, mapValue, valueName, ""); err != nil {
216 func (q *queryParser) parseScalar(v url.Values, r reflect.Value, name string, tag reflect.StructTag) error {
217 switch value := r.Interface().(type) {
222 v.Set(name, base64.StdEncoding.EncodeToString(value))
225 v.Set(name, strconv.FormatBool(value))
227 v.Set(name, strconv.FormatInt(value, 10))
229 v.Set(name, strconv.Itoa(value))
231 v.Set(name, strconv.FormatFloat(value, 'f', -1, 64))
233 v.Set(name, strconv.FormatFloat(float64(value), 'f', -1, 32))
235 const ISO8601UTC = "2006-01-02T15:04:05Z"
236 format := tag.Get("timestampFormat")
237 if len(format) == 0 {
238 format = protocol.ISO8601TimeFormatName
241 v.Set(name, protocol.FormatTime(format, value))
243 return fmt.Errorf("unsupported value for param %s: %v (%s)", name, r.Interface(), r.Type().Name())