encoder.go (5772B)
1 package form 2 3 import ( 4 "fmt" 5 "net/url" 6 "reflect" 7 "strconv" 8 "time" 9 ) 10 11 type encoder struct { 12 e *Encoder 13 errs EncodeErrors 14 values url.Values 15 namespace []byte 16 } 17 18 func (e *encoder) setError(namespace []byte, err error) { 19 if e.errs == nil { 20 e.errs = make(EncodeErrors) 21 } 22 23 e.errs[string(namespace)] = err 24 } 25 26 func (e *encoder) setVal(namespace []byte, idx int, vals ...string) { 27 28 arr, ok := e.values[string(namespace)] 29 if ok { 30 arr = append(arr, vals...) 31 } else { 32 arr = vals 33 } 34 35 e.values[string(namespace)] = arr 36 } 37 38 func (e *encoder) traverseStruct(v reflect.Value, namespace []byte, idx int) { 39 40 typ := v.Type() 41 l := len(namespace) 42 first := l == 0 43 44 // anonymous structs will still work for caching as the whole definition is stored 45 // including tags 46 s, ok := e.e.structCache.Get(typ) 47 if !ok { 48 s = e.e.structCache.parseStruct(e.e.mode, v, typ, e.e.tagName) 49 } 50 51 for _, f := range s.fields { 52 namespace = namespace[:l] 53 54 if f.isAnonymous && e.e.embedAnonymous { 55 e.setFieldByType(v.Field(f.idx), namespace, idx, f.isOmitEmpty) 56 continue 57 } 58 59 if first { 60 namespace = append(namespace, f.name...) 61 } else { 62 namespace = append(namespace, e.e.namespacePrefix...) 63 namespace = append(namespace, f.name...) 64 namespace = append(namespace, e.e.namespaceSuffix...) 65 } 66 67 e.setFieldByType(v.Field(f.idx), namespace, idx, f.isOmitEmpty) 68 } 69 } 70 71 func (e *encoder) setFieldByType(current reflect.Value, namespace []byte, idx int, isOmitEmpty bool) { 72 73 if idx > -1 && current.Kind() == reflect.Ptr { 74 namespace = append(namespace, '[') 75 namespace = strconv.AppendInt(namespace, int64(idx), 10) 76 namespace = append(namespace, ']') 77 idx = -2 78 } 79 80 if isOmitEmpty && !hasValue(current) { 81 return 82 } 83 v, kind := ExtractType(current) 84 85 if e.e.customTypeFuncs != nil { 86 87 if cf, ok := e.e.customTypeFuncs[v.Type()]; ok { 88 89 arr, err := cf(v.Interface()) 90 if err != nil { 91 e.setError(namespace, err) 92 return 93 } 94 95 if idx > -1 { 96 namespace = append(namespace, '[') 97 namespace = strconv.AppendInt(namespace, int64(idx), 10) 98 namespace = append(namespace, ']') 99 } 100 101 e.setVal(namespace, idx, arr...) 102 return 103 } 104 } 105 106 switch kind { 107 case reflect.Ptr, reflect.Interface, reflect.Invalid: 108 return 109 110 case reflect.String: 111 112 e.setVal(namespace, idx, v.String()) 113 114 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 115 116 e.setVal(namespace, idx, strconv.FormatUint(v.Uint(), 10)) 117 118 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 119 120 e.setVal(namespace, idx, strconv.FormatInt(v.Int(), 10)) 121 122 case reflect.Float32: 123 124 e.setVal(namespace, idx, strconv.FormatFloat(v.Float(), 'f', -1, 32)) 125 126 case reflect.Float64: 127 128 e.setVal(namespace, idx, strconv.FormatFloat(v.Float(), 'f', -1, 64)) 129 130 case reflect.Bool: 131 132 e.setVal(namespace, idx, strconv.FormatBool(v.Bool())) 133 134 case reflect.Slice, reflect.Array: 135 136 if idx == -1 { 137 138 for i := 0; i < v.Len(); i++ { 139 e.setFieldByType(v.Index(i), namespace, i, false) 140 } 141 142 return 143 } 144 145 if idx > -1 { 146 namespace = append(namespace, '[') 147 namespace = strconv.AppendInt(namespace, int64(idx), 10) 148 namespace = append(namespace, ']') 149 } 150 151 namespace = append(namespace, '[') 152 l := len(namespace) 153 154 for i := 0; i < v.Len(); i++ { 155 namespace = namespace[:l] 156 namespace = strconv.AppendInt(namespace, int64(i), 10) 157 namespace = append(namespace, ']') 158 e.setFieldByType(v.Index(i), namespace, -2, false) 159 } 160 161 case reflect.Map: 162 163 if idx > -1 { 164 namespace = append(namespace, '[') 165 namespace = strconv.AppendInt(namespace, int64(idx), 10) 166 namespace = append(namespace, ']') 167 } 168 169 var valid bool 170 var s string 171 l := len(namespace) 172 173 for _, key := range v.MapKeys() { 174 175 namespace = namespace[:l] 176 177 if s, valid = e.getMapKey(key, namespace); !valid { 178 continue 179 } 180 181 namespace = append(namespace, '[') 182 namespace = append(namespace, s...) 183 namespace = append(namespace, ']') 184 185 e.setFieldByType(v.MapIndex(key), namespace, -2, false) 186 } 187 188 case reflect.Struct: 189 190 // if we get here then no custom time function declared so use RFC3339 by default 191 if v.Type() == timeType { 192 193 if idx > -1 { 194 namespace = append(namespace, '[') 195 namespace = strconv.AppendInt(namespace, int64(idx), 10) 196 namespace = append(namespace, ']') 197 } 198 199 e.setVal(namespace, idx, v.Interface().(time.Time).Format(time.RFC3339)) 200 return 201 } 202 203 if idx == -1 { 204 e.traverseStruct(v, namespace, idx) 205 return 206 } 207 208 if idx > -1 { 209 namespace = append(namespace, '[') 210 namespace = strconv.AppendInt(namespace, int64(idx), 10) 211 namespace = append(namespace, ']') 212 } 213 214 e.traverseStruct(v, namespace, -2) 215 } 216 } 217 218 func (e *encoder) getMapKey(key reflect.Value, namespace []byte) (string, bool) { 219 220 v, kind := ExtractType(key) 221 222 if e.e.customTypeFuncs != nil { 223 224 if cf, ok := e.e.customTypeFuncs[v.Type()]; ok { 225 arr, err := cf(v.Interface()) 226 if err != nil { 227 e.setError(namespace, err) 228 return "", false 229 } 230 231 return arr[0], true 232 } 233 } 234 235 switch kind { 236 case reflect.Interface, reflect.Ptr: 237 return "", false 238 239 case reflect.String: 240 return v.String(), true 241 242 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 243 return strconv.FormatUint(v.Uint(), 10), true 244 245 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 246 return strconv.FormatInt(v.Int(), 10), true 247 248 case reflect.Float32: 249 return strconv.FormatFloat(v.Float(), 'f', -1, 32), true 250 251 case reflect.Float64: 252 return strconv.FormatFloat(v.Float(), 'f', -1, 64), true 253 254 case reflect.Bool: 255 return strconv.FormatBool(v.Bool()), true 256 257 default: 258 e.setError(namespace, fmt.Errorf("Unsupported Map Key '%v' Namespace '%s'", v.String(), namespace)) 259 return "", false 260 } 261 }