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 // check for unflattened list member
125 if !q.isEC2 && tag.Get("flattened") == "" {
126 if listName := tag.Get("locationNameList"); listName == "" {
129 prefix += "." + listName
133 for i := 0; i < value.Len(); i++ {
134 slicePrefix := prefix
135 if slicePrefix == "" {
136 slicePrefix = strconv.Itoa(i + 1)
138 slicePrefix = slicePrefix + "." + strconv.Itoa(i+1)
140 if err := q.parseValue(v, value.Index(i), slicePrefix, ""); err != nil {
147 func (q *queryParser) parseMap(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
148 // If it's empty, generate an empty value
149 if !value.IsNil() && value.Len() == 0 {
154 // check for unflattened list member
155 if !q.isEC2 && tag.Get("flattened") == "" {
159 // sort keys for improved serialization consistency.
160 // this is not strictly necessary for protocol support.
161 mapKeyValues := value.MapKeys()
162 mapKeys := map[string]reflect.Value{}
163 mapKeyNames := make([]string, len(mapKeyValues))
164 for i, mapKey := range mapKeyValues {
165 name := mapKey.String()
166 mapKeys[name] = mapKey
167 mapKeyNames[i] = name
169 sort.Strings(mapKeyNames)
171 for i, mapKeyName := range mapKeyNames {
172 mapKey := mapKeys[mapKeyName]
173 mapValue := value.MapIndex(mapKey)
175 kname := tag.Get("locationNameKey")
179 vname := tag.Get("locationNameValue")
187 keyName = strconv.Itoa(i+1) + "." + kname
189 keyName = prefix + "." + strconv.Itoa(i+1) + "." + kname
192 if err := q.parseValue(v, mapKey, keyName, ""); err != nil {
199 valueName = strconv.Itoa(i+1) + "." + vname
201 valueName = prefix + "." + strconv.Itoa(i+1) + "." + vname
204 if err := q.parseValue(v, mapValue, valueName, ""); err != nil {
212 func (q *queryParser) parseScalar(v url.Values, r reflect.Value, name string, tag reflect.StructTag) error {
213 switch value := r.Interface().(type) {
218 v.Set(name, base64.StdEncoding.EncodeToString(value))
221 v.Set(name, strconv.FormatBool(value))
223 v.Set(name, strconv.FormatInt(value, 10))
225 v.Set(name, strconv.Itoa(value))
227 v.Set(name, strconv.FormatFloat(value, 'f', -1, 64))
229 v.Set(name, strconv.FormatFloat(float64(value), 'f', -1, 32))
231 const ISO8601UTC = "2006-01-02T15:04:05Z"
232 v.Set(name, value.UTC().Format(ISO8601UTC))
234 return fmt.Errorf("unsupported value for param %s: %v (%s)", name, r.Interface(), r.Type().Name())