github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/conversion/queryparams/convert.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 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 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 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. 15 */ 16 17 package queryparams 18 19 import ( 20 "fmt" 21 "net/url" 22 "reflect" 23 "strings" 24 ) 25 26 // Marshaler converts an object to a query parameter string representation 27 type Marshaler interface { 28 MarshalQueryParameter() (string, error) 29 } 30 31 // Unmarshaler converts a string representation to an object 32 type Unmarshaler interface { 33 UnmarshalQueryParameter(string) error 34 } 35 36 func jsonTag(field reflect.StructField) (string, bool) { 37 structTag := field.Tag.Get("json") 38 if len(structTag) == 0 { 39 return "", false 40 } 41 parts := strings.Split(structTag, ",") 42 tag := parts[0] 43 if tag == "-" { 44 tag = "" 45 } 46 omitempty := false 47 parts = parts[1:] 48 for _, part := range parts { 49 if part == "omitempty" { 50 omitempty = true 51 break 52 } 53 } 54 return tag, omitempty 55 } 56 57 func isPointerKind(kind reflect.Kind) bool { 58 return kind == reflect.Pointer 59 } 60 61 func isStructKind(kind reflect.Kind) bool { 62 return kind == reflect.Struct 63 } 64 65 func isValueKind(kind reflect.Kind) bool { 66 switch kind { 67 case reflect.String, reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, 68 reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, 69 reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, 70 reflect.Float64, reflect.Complex64, reflect.Complex128: 71 return true 72 default: 73 return false 74 } 75 } 76 77 func zeroValue(value reflect.Value) bool { 78 return reflect.DeepEqual(reflect.Zero(value.Type()).Interface(), value.Interface()) 79 } 80 81 func customMarshalValue(value reflect.Value) (reflect.Value, bool) { 82 // Return unless we implement a custom query marshaler 83 if !value.CanInterface() { 84 return reflect.Value{}, false 85 } 86 87 marshaler, ok := value.Interface().(Marshaler) 88 if !ok { 89 if !isPointerKind(value.Kind()) && value.CanAddr() { 90 marshaler, ok = value.Addr().Interface().(Marshaler) 91 if !ok { 92 return reflect.Value{}, false 93 } 94 } else { 95 return reflect.Value{}, false 96 } 97 } 98 99 // Don't invoke functions on nil pointers 100 // If the type implements MarshalQueryParameter, AND the tag is not omitempty, AND the value is a nil pointer, "" seems like a reasonable response 101 if isPointerKind(value.Kind()) && zeroValue(value) { 102 return reflect.ValueOf(""), true 103 } 104 105 // Get the custom marshalled value 106 v, err := marshaler.MarshalQueryParameter() 107 if err != nil { 108 return reflect.Value{}, false 109 } 110 return reflect.ValueOf(v), true 111 } 112 113 func addParam(values url.Values, tag string, omitempty bool, value reflect.Value) { 114 if omitempty && zeroValue(value) { 115 return 116 } 117 val := "" 118 iValue := fmt.Sprintf("%v", value.Interface()) 119 120 if iValue != "<nil>" { 121 val = iValue 122 } 123 values.Add(tag, val) 124 } 125 126 func addListOfParams(values url.Values, tag string, omitempty bool, list reflect.Value) { 127 for i := 0; i < list.Len(); i++ { 128 addParam(values, tag, omitempty, list.Index(i)) 129 } 130 } 131 132 // Convert takes an object and converts it to a url.Values object using JSON tags as 133 // parameter names. Only top-level simple values, arrays, and slices are serialized. 134 // Embedded structs, maps, etc. will not be serialized. 135 func Convert(obj interface{}) (url.Values, error) { 136 result := url.Values{} 137 if obj == nil { 138 return result, nil 139 } 140 var sv reflect.Value 141 switch reflect.TypeOf(obj).Kind() { 142 case reflect.Pointer, reflect.Interface: 143 sv = reflect.ValueOf(obj).Elem() 144 default: 145 return nil, fmt.Errorf("expecting a pointer or interface") 146 } 147 st := sv.Type() 148 if !isStructKind(st.Kind()) { 149 return nil, fmt.Errorf("expecting a pointer to a struct") 150 } 151 152 // Check all object fields 153 convertStruct(result, st, sv) 154 155 return result, nil 156 } 157 158 func convertStruct(result url.Values, st reflect.Type, sv reflect.Value) { 159 for i := 0; i < st.NumField(); i++ { 160 field := sv.Field(i) 161 tag, omitempty := jsonTag(st.Field(i)) 162 if len(tag) == 0 { 163 continue 164 } 165 ft := field.Type() 166 167 kind := ft.Kind() 168 if isPointerKind(kind) { 169 ft = ft.Elem() 170 kind = ft.Kind() 171 if !field.IsNil() { 172 field = reflect.Indirect(field) 173 // If the field is non-nil, it should be added to params 174 // and the omitempty should be overwite to false 175 omitempty = false 176 } 177 } 178 179 switch { 180 case isValueKind(kind): 181 addParam(result, tag, omitempty, field) 182 case kind == reflect.Array || kind == reflect.Slice: 183 if isValueKind(ft.Elem().Kind()) { 184 addListOfParams(result, tag, omitempty, field) 185 } 186 case isStructKind(kind) && !(zeroValue(field) && omitempty): 187 if marshalValue, ok := customMarshalValue(field); ok { 188 addParam(result, tag, omitempty, marshalValue) 189 } else { 190 convertStruct(result, ft, field) 191 } 192 } 193 } 194 }