2 Copyright 2014 The Kubernetes Authors.
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
26 // Marshaler converts an object to a query parameter string representation
27 type Marshaler interface {
28 MarshalQueryParameter() (string, error)
31 // Unmarshaler converts a string representation to an object
32 type Unmarshaler interface {
33 UnmarshalQueryParameter(string) error
36 func jsonTag(field reflect.StructField) (string, bool) {
37 structTag := field.Tag.Get("json")
38 if len(structTag) == 0 {
41 parts := strings.Split(structTag, ",")
48 for _, part := range parts {
49 if part == "omitempty" {
57 func formatValue(value interface{}) string {
58 return fmt.Sprintf("%v", value)
61 func isPointerKind(kind reflect.Kind) bool {
62 return kind == reflect.Ptr
65 func isStructKind(kind reflect.Kind) bool {
66 return kind == reflect.Struct
69 func isValueKind(kind reflect.Kind) bool {
71 case reflect.String, reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16,
72 reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8,
73 reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32,
74 reflect.Float64, reflect.Complex64, reflect.Complex128:
81 func zeroValue(value reflect.Value) bool {
82 return reflect.DeepEqual(reflect.Zero(value.Type()).Interface(), value.Interface())
85 func customMarshalValue(value reflect.Value) (reflect.Value, bool) {
86 // Return unless we implement a custom query marshaler
87 if !value.CanInterface() {
88 return reflect.Value{}, false
91 marshaler, ok := value.Interface().(Marshaler)
93 if !isPointerKind(value.Kind()) && value.CanAddr() {
94 marshaler, ok = value.Addr().Interface().(Marshaler)
96 return reflect.Value{}, false
99 return reflect.Value{}, false
103 // Don't invoke functions on nil pointers
104 // If the type implements MarshalQueryParameter, AND the tag is not omitempty, AND the value is a nil pointer, "" seems like a reasonable response
105 if isPointerKind(value.Kind()) && zeroValue(value) {
106 return reflect.ValueOf(""), true
109 // Get the custom marshalled value
110 v, err := marshaler.MarshalQueryParameter()
112 return reflect.Value{}, false
114 return reflect.ValueOf(v), true
117 func addParam(values url.Values, tag string, omitempty bool, value reflect.Value) {
118 if omitempty && zeroValue(value) {
122 iValue := fmt.Sprintf("%v", value.Interface())
124 if iValue != "<nil>" {
130 func addListOfParams(values url.Values, tag string, omitempty bool, list reflect.Value) {
131 for i := 0; i < list.Len(); i++ {
132 addParam(values, tag, omitempty, list.Index(i))
136 // Convert takes an object and converts it to a url.Values object using JSON tags as
137 // parameter names. Only top-level simple values, arrays, and slices are serialized.
138 // Embedded structs, maps, etc. will not be serialized.
139 func Convert(obj interface{}) (url.Values, error) {
140 result := url.Values{}
145 switch reflect.TypeOf(obj).Kind() {
146 case reflect.Ptr, reflect.Interface:
147 sv = reflect.ValueOf(obj).Elem()
149 return nil, fmt.Errorf("expecting a pointer or interface")
152 if !isStructKind(st.Kind()) {
153 return nil, fmt.Errorf("expecting a pointer to a struct")
156 // Check all object fields
157 convertStruct(result, st, sv)
162 func convertStruct(result url.Values, st reflect.Type, sv reflect.Value) {
163 for i := 0; i < st.NumField(); i++ {
165 tag, omitempty := jsonTag(st.Field(i))
172 if isPointerKind(kind) {
176 field = reflect.Indirect(field)
177 // If the field is non-nil, it should be added to params
178 // and the omitempty should be overwite to false
184 case isValueKind(kind):
185 addParam(result, tag, omitempty, field)
186 case kind == reflect.Array || kind == reflect.Slice:
187 if isValueKind(ft.Elem().Kind()) {
188 addListOfParams(result, tag, omitempty, field)
190 case isStructKind(kind) && !(zeroValue(field) && omitempty):
191 if marshalValue, ok := customMarshalValue(field); ok {
192 addParam(result, tag, omitempty, marshalValue)
194 convertStruct(result, ft, field)