github.com/vmware/govmomi@v0.43.0/vim25/json/discriminator_test.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package json_test 6 7 import ( 8 "bytes" 9 "fmt" 10 "reflect" 11 "strconv" 12 "strings" 13 "testing" 14 15 json "github.com/vmware/govmomi/vim25/json" 16 ) 17 18 type DS1 struct { 19 F1 interface{} `json:"f1"` 20 } 21 22 type DS2 struct { 23 F1 noop1 `json:"f1"` 24 } 25 26 type DS3 struct { 27 F1 string `json:"f1"` 28 } 29 30 type DS3Noop1 DS3 31 32 func (v DS3Noop1) noop1() {} 33 34 type DS4 struct { 35 F1 string `json:"f1"` 36 F2 interface{} `json:"f2"` 37 } 38 39 type DS4Noop1 DS4 40 41 func (v DS4Noop1) noop1() {} 42 43 type DS5 struct { 44 F1 string `json:"f1"` 45 F2 noop1 `json:"f2"` 46 } 47 48 type DS5Noop1 DS5 49 50 func (v DS5Noop1) noop1() {} 51 52 type DS6 struct { 53 F1 emptyIface `json:"f1"` 54 } 55 56 type DS7 struct { 57 F1 noop2 `json:"f1"` 58 } 59 60 type DS8 struct { 61 F1 DS3 `json:"f1"` 62 } 63 64 type DS9 struct { 65 F1 int64 66 } 67 68 func (d DS9) MarshalJSON() ([]byte, error) { 69 var b bytes.Buffer 70 b.WriteString(strconv.FormatInt(d.F1, 10)) 71 return b.Bytes(), nil 72 } 73 func (d *DS9) UnmarshalJSON(b []byte) error { 74 s := string(b) 75 if s == "null" { 76 d.F1 = 0 77 return nil 78 } 79 if len(s) < 1 { 80 return fmt.Errorf("Cannot parse empty as int64") 81 } 82 v, e := strconv.ParseInt(s, 10, 64) 83 if e != nil { 84 return fmt.Errorf("Cannot parse as int64: %v; %w", s, e) 85 } 86 d.F1 = v 87 return nil 88 } 89 90 // Struct implementing UnmarshalJSON with value receiver. 91 type DS10 struct { 92 DS9 *DS9 93 } 94 95 func (d DS10) UnmarshalJSON(b []byte) error { 96 if d.DS9 == nil { 97 return nil 98 } 99 return d.DS9.UnmarshalJSON(b) 100 } 101 func (d DS10) MarshalJSON() ([]byte, error) { 102 if d.DS9 == nil { 103 return []byte("null"), nil 104 } 105 return d.DS9.MarshalJSON() 106 } 107 108 type HexInt64 int64 109 110 func (i HexInt64) MarshalJSON() ([]byte, error) { 111 var b bytes.Buffer 112 b.WriteString(fmt.Sprintf(`"%X"`, i)) 113 return b.Bytes(), nil 114 } 115 116 func (i *HexInt64) UnmarshalJSON(b []byte) error { 117 s := string(b) 118 if s == "null" { 119 *i = 0 120 return nil 121 } 122 last := len(s) - 1 123 if last < 1 || s[0] != '"' || s[last] != '"' { 124 return fmt.Errorf("Cannot parse as hex int64: %v", s) 125 } 126 v, e := strconv.ParseInt(s[1:last], 16, 64) 127 if e != nil { 128 return fmt.Errorf("Cannot parse as hex int64: %v; %w", s, e) 129 } 130 *i = HexInt64(v) 131 return nil 132 } 133 134 func customNameWithFilter(t reflect.Type) string { 135 res := json.DefaultDiscriminatorFunc(t) 136 if res == "DS3" { 137 return "" 138 } 139 return res 140 } 141 142 var discriminatorTests = []struct { 143 obj interface{} 144 str string 145 expObj interface{} 146 expStr string 147 expEncErr string 148 expDecErr string 149 tf string 150 vf string 151 mode json.DiscriminatorEncodeMode 152 dd bool 153 discriminatorFunc json.TypeToDiscriminatorFunc 154 }{ 155 156 // invalid type/value combinations 157 {obj: true, str: `{"_t":"string","_v":true}`, expStr: `{"_t":"bool","_v":true}`, expDecErr: `json: cannot unmarshal bool into Go value of type string`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 158 {obj: DS1{F1: true}, str: `{"f1":{"_t":"string","_v":true}}`, expStr: `{"f1":{"_t":"bool","_v":true}}`, expDecErr: `json: cannot unmarshal bool into Go struct field DS1.f1 of type string`}, 159 160 // encode/decode nil/null works as expected 161 {obj: nil, str: `null`}, 162 {obj: nil, str: `null`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 163 164 // encode/decode number works as expected 165 {obj: float64(3), str: `3`}, 166 167 // encode/decode empty string works as expected 168 {obj: "", str: `""`}, 169 170 // encode/decode boolean works as expected 171 {obj: true, str: `true`}, 172 {obj: false, str: `false`}, 173 174 // primitive values with root object encoded with type name 175 {obj: uint(1), str: `{"_t":"uint","_v":1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 176 {obj: uint8(1), str: `{"_t":"uint8","_v":1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 177 {obj: uint16(1), str: `{"_t":"uint16","_v":1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 178 {obj: uint32(1), str: `{"_t":"uint32","_v":1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 179 {obj: uint64(1), str: `{"_t":"uint64","_v":1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 180 {obj: uintptr(1), str: `{"_t":"uintptr","_v":1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 181 182 {obj: int(-1), str: `{"_t":"int","_v":-1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 183 {obj: int8(-1), str: `{"_t":"int8","_v":-1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 184 {obj: int16(-1), str: `{"_t":"int16","_v":-1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 185 {obj: int32(-1), str: `{"_t":"int32","_v":-1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 186 {obj: int64(-1), str: `{"_t":"int64","_v":-1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 187 188 {obj: float32(-1.0), str: `{"_t":"float32","_v":-1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 189 {obj: float64(1.0), str: `{"_t":"float64","_v":1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 190 {obj: float32(-1.1), str: `{"_t":"float32","_v":-1.1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 191 {obj: float64(1.1), str: `{"_t":"float64","_v":1.1}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 192 193 {obj: "hello", str: `{"_t":"string","_v":"hello"}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 194 {obj: true, str: `{"_t":"bool","_v":true}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 195 196 {obj: HexInt64(42), str: `{"_t":"HexInt64","_v":"2A"}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 197 {obj: DS9{F1: 42}, str: `{"_t":"DS9","_v":42}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 198 {obj: DS6{F1: DS9{F1: 42}}, str: `{"f1":{"_t":"DS9","_v":42}}`}, 199 {obj: DS9{F1: 42}, str: `42`}, 200 201 {obj: DS10{DS9: &DS9{F1: 42}}, str: `42`}, 202 {obj: DS6{F1: DS10{DS9: &DS9{F1: 42}}}, str: `{"f1":{"_t":"DS10","_v":42}}`, expObj: DS6{F1: DS10{DS9: nil}}}, 203 204 // primitive values stored in interface with 0 methods 205 {obj: DS1{F1: uint(1)}, str: `{"f1":{"_t":"uint","_v":1}}`}, 206 {obj: DS1{F1: uint8(1)}, str: `{"f1":{"_t":"uint8","_v":1}}`}, 207 {obj: DS1{F1: uint16(1)}, str: `{"f1":{"_t":"uint16","_v":1}}`}, 208 {obj: DS1{F1: uint32(1)}, str: `{"f1":{"_t":"uint32","_v":1}}`}, 209 {obj: DS1{F1: uint64(1)}, str: `{"f1":{"_t":"uint64","_v":1}}`}, 210 {obj: DS1{F1: uintptr(1)}, str: `{"f1":{"_t":"uintptr","_v":1}}`}, 211 212 {obj: DS6{F1: int(-1)}, str: `{"f1":{"_t":"int","_v":-1}}`}, 213 {obj: DS6{F1: int8(-1)}, str: `{"f1":{"_t":"int8","_v":-1}}`}, 214 {obj: DS6{F1: int16(-1)}, str: `{"f1":{"_t":"int16","_v":-1}}`}, 215 {obj: DS6{F1: int32(-1)}, str: `{"f1":{"_t":"int32","_v":-1}}`}, 216 {obj: DS6{F1: int64(-1)}, str: `{"f1":{"_t":"int64","_v":-1}}`}, 217 218 {obj: DS1{F1: float32(-1.0)}, str: `{"f1":{"_t":"float32","_v":-1}}`}, 219 {obj: DS1{F1: float64(1.0)}, str: `{"f1":{"_t":"float64","_v":1}}`}, 220 {obj: DS1{F1: float32(-1.1)}, str: `{"f1":{"_t":"float32","_v":-1.1}}`}, 221 {obj: DS1{F1: float64(1.1)}, str: `{"f1":{"_t":"float64","_v":1.1}}`}, 222 223 {obj: DS1{F1: "hello"}, str: `{"f1":{"_t":"string","_v":"hello"}}`}, 224 {obj: DS1{F1: true}, str: `{"f1":{"_t":"bool","_v":true}}`}, 225 226 // address of primitive values stored in interface with 0 methods 227 {obj: DS1{F1: addrOfUint(1)}, str: `{"f1":{"_t":"uint","_v":1}}`, expObj: DS1{F1: uint(1)}}, 228 {obj: DS1{F1: addrOfUint8(1)}, str: `{"f1":{"_t":"uint8","_v":1}}`, expObj: DS1{F1: uint8(1)}}, 229 {obj: DS1{F1: addrOfUint16(1)}, str: `{"f1":{"_t":"uint16","_v":1}}`, expObj: DS1{F1: uint16(1)}}, 230 {obj: DS1{F1: addrOfUint32(1)}, str: `{"f1":{"_t":"uint32","_v":1}}`, expObj: DS1{F1: uint32(1)}}, 231 {obj: DS1{F1: addrOfUint64(1)}, str: `{"f1":{"_t":"uint64","_v":1}}`, expObj: DS1{F1: uint64(1)}}, 232 {obj: DS1{F1: addrOfUintptr(1)}, str: `{"f1":{"_t":"uintptr","_v":1}}`, expObj: DS1{F1: uintptr(1)}}, 233 234 {obj: DS6{F1: addrOfInt(-1)}, str: `{"f1":{"_t":"int","_v":-1}}`, expObj: DS6{F1: int(-1)}}, 235 {obj: DS6{F1: addrOfInt8(-1)}, str: `{"f1":{"_t":"int8","_v":-1}}`, expObj: DS6{F1: int8(-1)}}, 236 {obj: DS6{F1: addrOfInt16(-1)}, str: `{"f1":{"_t":"int16","_v":-1}}`, expObj: DS6{F1: int16(-1)}}, 237 {obj: DS6{F1: addrOfInt32(-1)}, str: `{"f1":{"_t":"int32","_v":-1}}`, expObj: DS6{F1: int32(-1)}}, 238 {obj: DS6{F1: addrOfInt64(-1)}, str: `{"f1":{"_t":"int64","_v":-1}}`, expObj: DS6{F1: int64(-1)}}, 239 240 {obj: DS1{F1: addrOfFloat32(-1.0)}, str: `{"f1":{"_t":"float32","_v":-1}}`, expObj: DS1{F1: float32(-1.0)}}, 241 {obj: DS1{F1: addrOfFloat64(1.0)}, str: `{"f1":{"_t":"float64","_v":1}}`, expObj: DS1{F1: float64(1)}}, 242 {obj: DS1{F1: addrOfFloat32(-1.1)}, str: `{"f1":{"_t":"float32","_v":-1.1}}`, expObj: DS1{F1: float32(-1.1)}}, 243 {obj: DS1{F1: addrOfFloat64(1.1)}, str: `{"f1":{"_t":"float64","_v":1.1}}`, expObj: DS1{F1: float64(1.1)}}, 244 245 {obj: DS1{F1: addrOfString("hello")}, str: `{"f1":{"_t":"string","_v":"hello"}}`, expObj: DS1{F1: "hello"}}, 246 {obj: DS1{F1: addrOfBool(true)}, str: `{"f1":{"_t":"bool","_v":true}}`, expObj: DS1{F1: true}}, 247 248 // primitive values stored in interface with >0 methods 249 {obj: DS2{F1: uintNoop(1)}, str: `{"f1":{"_t":"uintNoop","_v":1}}`}, 250 {obj: DS2{F1: uint8Noop(1)}, str: `{"f1":{"_t":"uint8Noop","_v":1}}`}, 251 {obj: DS2{F1: uint16Noop(1)}, str: `{"f1":{"_t":"uint16Noop","_v":1}}`}, 252 {obj: DS2{F1: uint32Noop(1)}, str: `{"f1":{"_t":"uint32Noop","_v":1}}`}, 253 {obj: DS2{F1: uint64Noop(1)}, str: `{"f1":{"_t":"uint64Noop","_v":1}}`}, 254 {obj: DS2{F1: uintptrNoop(1)}, str: `{"f1":{"_t":"uintptrNoop","_v":1}}`}, 255 256 {obj: DS2{F1: intNoop(1)}, str: `{"f1":{"_t":"intNoop","_v":1}}`}, 257 {obj: DS2{F1: int8Noop(1)}, str: `{"f1":{"_t":"int8Noop","_v":1}}`}, 258 {obj: DS2{F1: int16Noop(1)}, str: `{"f1":{"_t":"int16Noop","_v":1}}`}, 259 {obj: DS2{F1: int32Noop(1)}, str: `{"f1":{"_t":"int32Noop","_v":1}}`}, 260 {obj: DS2{F1: int64Noop(1)}, str: `{"f1":{"_t":"int64Noop","_v":1}}`}, 261 262 {obj: DS2{F1: float32Noop(-1.0)}, str: `{"f1":{"_t":"float32Noop","_v":-1}}`}, 263 {obj: DS2{F1: float64Noop(1.0)}, str: `{"f1":{"_t":"float64Noop","_v":1}}`}, 264 {obj: DS2{F1: float32Noop(-1.1)}, str: `{"f1":{"_t":"float32Noop","_v":-1.1}}`}, 265 {obj: DS2{F1: float64Noop(1.1)}, str: `{"f1":{"_t":"float64Noop","_v":1.1}}`}, 266 267 {obj: DS2{F1: stringNoop("hello")}, str: `{"f1":{"_t":"stringNoop","_v":"hello"}}`}, 268 {obj: DS2{F1: boolNoop(true)}, str: `{"f1":{"_t":"boolNoop","_v":true}}`}, 269 270 // address of primitive values stored in interface with >0 methods 271 {obj: DS2{F1: addrOfUintNoop(1)}, str: `{"f1":{"_t":"uintNoop","_v":1}}`, expObj: DS2{F1: uintNoop(1)}}, 272 {obj: DS2{F1: addrOfUint8Noop(1)}, str: `{"f1":{"_t":"uint8Noop","_v":1}}`, expObj: DS2{F1: uint8Noop(1)}}, 273 {obj: DS2{F1: addrOfUint16Noop(1)}, str: `{"f1":{"_t":"uint16Noop","_v":1}}`, expObj: DS2{F1: uint16Noop(1)}}, 274 {obj: DS2{F1: addrOfUint32Noop(1)}, str: `{"f1":{"_t":"uint32Noop","_v":1}}`, expObj: DS2{F1: uint32Noop(1)}}, 275 {obj: DS2{F1: addrOfUint64Noop(1)}, str: `{"f1":{"_t":"uint64Noop","_v":1}}`, expObj: DS2{F1: uint64Noop(1)}}, 276 {obj: DS2{F1: addrOfUintptrNoop(1)}, str: `{"f1":{"_t":"uintptrNoop","_v":1}}`, expObj: DS2{F1: uintptrNoop(1)}}, 277 278 {obj: DS2{F1: addrOfIntNoop(1)}, str: `{"f1":{"_t":"intNoop","_v":1}}`, expObj: DS2{F1: intNoop(1)}}, 279 {obj: DS2{F1: addrOfInt8Noop(1)}, str: `{"f1":{"_t":"int8Noop","_v":1}}`, expObj: DS2{F1: int8Noop(1)}}, 280 {obj: DS2{F1: addrOfInt16Noop(1)}, str: `{"f1":{"_t":"int16Noop","_v":1}}`, expObj: DS2{F1: int16Noop(1)}}, 281 {obj: DS2{F1: addrOfInt32Noop(1)}, str: `{"f1":{"_t":"int32Noop","_v":1}}`, expObj: DS2{F1: int32Noop(1)}}, 282 {obj: DS2{F1: addrOfInt64Noop(1)}, str: `{"f1":{"_t":"int64Noop","_v":1}}`, expObj: DS2{F1: int64Noop(1)}}, 283 284 {obj: DS2{F1: addrOfFloat32Noop(-1.0)}, str: `{"f1":{"_t":"float32Noop","_v":-1}}`, expObj: DS2{F1: float32Noop(-1.0)}}, 285 {obj: DS2{F1: addrOfFloat64Noop(1.0)}, str: `{"f1":{"_t":"float64Noop","_v":1}}`, expObj: DS2{F1: float64Noop(1.0)}}, 286 {obj: DS2{F1: addrOfFloat32Noop(-1.1)}, str: `{"f1":{"_t":"float32Noop","_v":-1.1}}`, expObj: DS2{F1: float32Noop(-1.1)}}, 287 {obj: DS2{F1: addrOfFloat64Noop(1.1)}, str: `{"f1":{"_t":"float64Noop","_v":1.1}}`, expObj: DS2{F1: float64Noop(1.1)}}, 288 289 {obj: DS2{F1: addrOfStringNoop("hello")}, str: `{"f1":{"_t":"stringNoop","_v":"hello"}}`, expObj: DS2{F1: stringNoop("hello")}}, 290 {obj: DS2{F1: addrOfBoolNoop(true)}, str: `{"f1":{"_t":"boolNoop","_v":true}}`, expObj: DS2{F1: boolNoop(true)}}, 291 292 // address of primitive values stored in interface with >0 methods where only the address 293 // of the value satisfies the interface. 294 // 295 // Unmarshaling the JSON into the object causes the decoder to check to see if the JSON objects 296 // can be stored in DS7.F1, finding out that they cannot due to all the types implementing 297 // DS7.F1 by-address. Thus the decoder will then check to see if the value can be assigned to 298 // DS7.F1 by-address, which will work. 299 {obj: DS7{F1: addrOfUintNoop(1)}, str: `{"f1":{"_t":"uintNoop","_v":1}}`}, 300 {obj: DS7{F1: addrOfUint8Noop(1)}, str: `{"f1":{"_t":"uint8Noop","_v":1}}`}, 301 {obj: DS7{F1: addrOfUint16Noop(1)}, str: `{"f1":{"_t":"uint16Noop","_v":1}}`}, 302 {obj: DS7{F1: addrOfUint32Noop(1)}, str: `{"f1":{"_t":"uint32Noop","_v":1}}`}, 303 {obj: DS7{F1: addrOfUint64Noop(1)}, str: `{"f1":{"_t":"uint64Noop","_v":1}}`}, 304 {obj: DS7{F1: addrOfUintptrNoop(1)}, str: `{"f1":{"_t":"uintptrNoop","_v":1}}`}, 305 306 {obj: DS7{F1: addrOfIntNoop(1)}, str: `{"f1":{"_t":"intNoop","_v":1}}`}, 307 {obj: DS7{F1: addrOfInt8Noop(1)}, str: `{"f1":{"_t":"int8Noop","_v":1}}`}, 308 {obj: DS7{F1: addrOfInt16Noop(1)}, str: `{"f1":{"_t":"int16Noop","_v":1}}`}, 309 {obj: DS7{F1: addrOfInt32Noop(1)}, str: `{"f1":{"_t":"int32Noop","_v":1}}`}, 310 {obj: DS7{F1: addrOfInt64Noop(1)}, str: `{"f1":{"_t":"int64Noop","_v":1}}`}, 311 312 {obj: DS7{F1: addrOfFloat32Noop(-1.0)}, str: `{"f1":{"_t":"float32Noop","_v":-1}}`}, 313 {obj: DS7{F1: addrOfFloat64Noop(1.0)}, str: `{"f1":{"_t":"float64Noop","_v":1}}`}, 314 {obj: DS7{F1: addrOfFloat32Noop(-1.1)}, str: `{"f1":{"_t":"float32Noop","_v":-1.1}}`}, 315 {obj: DS7{F1: addrOfFloat64Noop(1.1)}, str: `{"f1":{"_t":"float64Noop","_v":1.1}}`}, 316 317 {obj: DS7{F1: addrOfStringNoop("hello")}, str: `{"f1":{"_t":"stringNoop","_v":"hello"}}`}, 318 {obj: DS7{F1: addrOfBoolNoop(true)}, str: `{"f1":{"_t":"boolNoop","_v":true}}`}, 319 320 // struct value stored in interface with 0 methods 321 {obj: DS1{F1: DS3{F1: "hello"}}, str: `{"f1":{"_t":"DS3","f1":"hello"}}`}, 322 {obj: DS1{F1: DS3{F1: "hello"}}, str: `{"_t":"DS1","f1":{"_t":"DS3","f1":"hello"}}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 323 324 {obj: DS1{F1: DS4{F1: "hello", F2: int(1)}}, str: `{"f1":{"_t":"DS4","f1":"hello","f2":{"_t":"int","_v":1}}}`}, 325 {obj: DS1{F1: DS4{F1: "hello", F2: DS3{F1: "world"}}}, str: `{"f1":{"_t":"DS4","f1":"hello","f2":{"_t":"DS3","f1":"world"}}}`}, 326 327 // struct value stored in interface with >0 methods 328 {obj: DS2{F1: DS3Noop1{F1: "hello"}}, str: `{"f1":{"_t":"DS3Noop1","f1":"hello"}}`}, 329 {obj: DS2{F1: DS4Noop1{F1: "hello", F2: int(1)}}, str: `{"f1":{"_t":"DS4Noop1","f1":"hello","f2":{"_t":"int","_v":1}}}`}, 330 {obj: DS2{F1: DS5Noop1{F1: "hello", F2: DS3Noop1{F1: "world"}}}, str: `{"f1":{"_t":"DS5Noop1","f1":"hello","f2":{"_t":"DS3Noop1","f1":"world"}}}`}, 331 332 // address of struct value stored in interface with 0 methods 333 {obj: DS1{F1: &DS3{F1: "hello"}}, str: `{"f1":{"_t":"DS3","f1":"hello"}}`, expObj: DS1{F1: DS3{F1: "hello"}}}, 334 {obj: DS1{F1: DS4{F1: "hello", F2: &DS3{F1: "world"}}}, str: `{"f1":{"_t":"DS4","f1":"hello","f2":{"_t":"DS3","f1":"world"}}}`, expObj: DS1{F1: DS4{F1: "hello", F2: DS3{F1: "world"}}}}, 335 336 // address of struct value stored in interface with >0 methods 337 {obj: DS2{F1: DS3Noop1{F1: "hello"}}, str: `{"f1":{"_t":"DS3Noop1","f1":"hello"}}`}, 338 {obj: DS2{F1: DS4Noop1{F1: "hello", F2: int(1)}}, str: `{"f1":{"_t":"DS4Noop1","f1":"hello","f2":{"_t":"int","_v":1}}}`}, 339 {obj: DS2{F1: DS5Noop1{F1: "hello", F2: DS3Noop1{F1: "world"}}}, str: `{"f1":{"_t":"DS5Noop1","f1":"hello","f2":{"_t":"DS3Noop1","f1":"world"}}}`}, 340 341 // slices stored in interface with 0 methods 342 {obj: DS1{F1: []int{1, 2, 3}}, str: `{"f1":{"_t":"[]int","_v":[1,2,3]}}`}, 343 {obj: DS1{F1: []*int{addrOfInt(1), addrOfInt(2), addrOfInt(3)}}, str: `{"f1":{"_t":"[]*int","_v":[1,2,3]}}`}, 344 {obj: DS1{F1: []interface{}{1, 2, 3}}, str: `{"f1":{"_t":"[]interface {}","_v":[{"_t":"int","_v":1},{"_t":"int","_v":2},{"_t":"int","_v":3}]}}`}, 345 346 // slices stored in interface with >0 methods 347 {obj: DS2{F1: sliceIntNoop{1, 2, 3}}, str: `{"f1":{"_t":"sliceIntNoop","_v":[1,2,3]}}`}, 348 349 // address of slices stored in interface with >0 methods where only the 350 // address of the value satisfies the interface 351 {obj: DS7{F1: addrOfSliceIntNoop(sliceIntNoop{1, 2, 3})}, str: `{"f1":{"_t":"sliceIntNoop","_v":[1,2,3]}}`}, 352 353 // arrays stored in interfaces with 0 methods 354 {obj: DS1{F1: [2]int{1, 2}}, str: `{"f1":{"_t":"[2]int","_v":[1,2]}}`}, 355 {obj: DS1{F1: [3]int{1, 2}}, str: `{"f1":{"_t":"[3]int","_v":[1,2,0]}}`}, 356 {obj: DS1{F1: [2]*int{addrOfInt(1), addrOfInt(2)}}, str: `{"f1":{"_t":"[2]*int","_v":[1,2]}}`}, 357 358 // arrays stored in interface with >0 methods 359 {obj: DS2{F1: arrayOfTwoIntsNoop{1, 2}}, str: `{"f1":{"_t":"arrayOfTwoIntsNoop","_v":[1,2]}}`}, 360 361 // address of arrays stored in interface with >0 methods where only the 362 // address of the value satisfies the interface 363 {obj: DS7{F1: addrOfArrayOfTwoIntsNoop(arrayOfTwoIntsNoop{1, 2})}, str: `{"f1":{"_t":"arrayOfTwoIntsNoop","_v":[1,2]}}`}, 364 365 // maps stored in interface with 0 methods 366 {obj: DS1{F1: map[string]int{"1": 1, "2": 2, "3": 3}}, str: `{"f1":{"_t":"map[string]int","1":1,"2":2,"3":3}}`}, 367 368 {obj: DS1{F1: map[string]*int{"1": addrOfInt(1), "2": addrOfInt(2), "3": addrOfInt(3)}}, str: `{"f1":{"_t":"map[string]*int","1":1,"2":2,"3":3}}`}, 369 {obj: DS1{F1: map[string]interface{}{"1": 1, "2": 2, "3": 3}}, str: `{"f1":{"_t":"map[string]interface {}","1":{"_t":"int","_v":1},"2":{"_t":"int","_v":2},"3":{"_t":"int","_v":3}}}`}, 370 // assert interface{} works during decode as well 371 {obj: DS1{F1: map[string]interface{}{"1": 1, "2": 2, "3": 3}}, str: `{"f1":{"_t":"map[string]interface{}","1":{"_t":"int","_v":1},"2":{"_t":"int","_v":2},"3":{"_t":"int","_v":3}}}`, expStr: `{"f1":{"_t":"map[string]interface {}","1":{"_t":"int","_v":1},"2":{"_t":"int","_v":2},"3":{"_t":"int","_v":3}}}`}, 372 373 // maps stored in interface with >0 methods 374 {obj: DS2{F1: mapStringIntNoop{"1": 1, "2": 2, "3": 3}}, str: `{"f1":{"_t":"mapStringIntNoop","1":1,"2":2,"3":3}}`}, 375 376 // address of maps stored in interface with >0 methods where only the 377 // address of the value satisfies the interface 378 {obj: DS7{F1: addrOfMapStringIntNoop(mapStringIntNoop{"1": 1, "2": 2, "3": 3})}, str: `{"f1":{"_t":"mapStringIntNoop","1":1,"2":2,"3":3}}`}, 379 380 // unsupported types 381 {obj: DS1{F1: complex64(1.0)}, str: `{"f1":{"_t":"complex64","_v":1.0}}`, expEncErr: "json: unsupported type: complex64", expDecErr: "json: unsupported discriminator type: complex64"}, 382 {obj: DS1{F1: complex128(1.0)}, str: `{"f1":{"_t":"complex128","_v":1.0}}`, expEncErr: "json: unsupported type: complex128", expDecErr: "json: unsupported discriminator type: complex128"}, 383 {obj: DS1{F1: make(chan struct{})}, str: `{"f1":{"_t":"chan struct {}","_v":null}}`, expEncErr: "json: unsupported value: invalid kind: chan", expDecErr: "json: invalid discriminator type: chan struct {}"}, 384 {obj: DS1{F1: func(string) {}}, str: `{"f1":{"_t":"func(string)","_v":null}}`, expEncErr: "json: unsupported value: invalid kind: func", expDecErr: "json: invalid discriminator type: func(string)"}, 385 386 // discriminator type not a string 387 {obj: DS1{}, str: `{"f1":{"_t":0,"_v":1}}`, expStr: `{"f1":null}`, expDecErr: "json: discriminator type at offset 12 is not string"}, 388 389 // discriminator not used for non-interface field 390 {obj: DS8{F1: DS3{F1: "hello"}}, str: `{"f1":{"f1":"hello"}}`}, 391 {obj: DS8{F1: DS3{F1: "hello"}}, str: `{"_t":"DS8","f1":{"f1":"hello"}}`, mode: json.DiscriminatorEncodeTypeNameRootValue}, 392 {obj: DS8{F1: DS3{F1: "hello"}}, str: `{"_t":"DS8","f1":{"_t":"DS3","f1":"hello"}}`, mode: json.DiscriminatorEncodeTypeNameRootValue | json.DiscriminatorEncodeTypeNameAllObjects}, 393 {obj: DS8{F1: DS3{F1: "hello"}}, str: `{"_t":"DS8","f1":{"f1":"hello"}}`, mode: json.DiscriminatorEncodeTypeNameRootValue | json.DiscriminatorEncodeTypeNameAllObjects, discriminatorFunc: customNameWithFilter}, 394 395 // discriminator with full type path 396 {obj: uint(1), str: `{"_t":"uint","_v":1}`, mode: json.DiscriminatorEncodeTypeNameRootValue, discriminatorFunc: json.FullName}, 397 {obj: DS2{F1: DS3Noop1{F1: "hello"}}, str: `{"f1":{"_t":"github.com/vmware/govmomi/vim25/json_test.DS3Noop1","f1":"hello"}}`, discriminatorFunc: json.FullName}, 398 } 399 400 func discriminatorToTypeFn(discriminator string) (reflect.Type, bool) { 401 switch discriminator { 402 case "DS1": 403 return reflect.TypeOf(DS1{}), true 404 case "DS2": 405 return reflect.TypeOf(DS2{}), true 406 case "DS3": 407 return reflect.TypeOf(DS3{}), true 408 case "DS3Noop1", "github.com/vmware/govmomi/vim25/json_test.DS3Noop1": 409 return reflect.TypeOf(DS3Noop1{}), true 410 case "DS4": 411 return reflect.TypeOf(DS4{}), true 412 case "DS4Noop1": 413 return reflect.TypeOf(DS4Noop1{}), true 414 case "DS5": 415 return reflect.TypeOf(DS5{}), true 416 case "DS5Noop1": 417 return reflect.TypeOf(DS5Noop1{}), true 418 case "DS6": 419 return reflect.TypeOf(DS6{}), true 420 case "DS7": 421 return reflect.TypeOf(DS7{}), true 422 case "DS8": 423 return reflect.TypeOf(DS8{}), true 424 case "DS9": 425 return reflect.TypeOf(DS9{}), true 426 case "DS10": 427 return reflect.TypeOf(DS10{}), true 428 case "uintNoop": 429 return reflect.TypeOf(uintNoop(0)), true 430 case "uint8Noop": 431 return reflect.TypeOf(uint8Noop(0)), true 432 case "uint16Noop": 433 return reflect.TypeOf(uint16Noop(0)), true 434 case "uint32Noop": 435 return reflect.TypeOf(uint32Noop(0)), true 436 case "uint64Noop": 437 return reflect.TypeOf(uint64Noop(0)), true 438 case "uintptrNoop": 439 return reflect.TypeOf(uintptrNoop(0)), true 440 case "intNoop": 441 return reflect.TypeOf(intNoop(0)), true 442 case "int8Noop": 443 return reflect.TypeOf(int8Noop(0)), true 444 case "int16Noop": 445 return reflect.TypeOf(int16Noop(0)), true 446 case "int32Noop": 447 return reflect.TypeOf(int32Noop(0)), true 448 case "int64Noop": 449 return reflect.TypeOf(int64Noop(0)), true 450 case "float32Noop": 451 return reflect.TypeOf(float32Noop(0)), true 452 case "float64Noop": 453 return reflect.TypeOf(float64Noop(0)), true 454 case "boolNoop": 455 return reflect.TypeOf(boolNoop(true)), true 456 case "stringNoop": 457 return reflect.TypeOf(stringNoop("")), true 458 case "mapStringIntNoop": 459 return reflect.TypeOf(mapStringIntNoop{}), true 460 case "sliceIntNoop": 461 return reflect.TypeOf(sliceIntNoop{}), true 462 case "arrayOfTwoIntsNoop": 463 return reflect.TypeOf(arrayOfTwoIntsNoop{}), true 464 case "HexInt64": 465 return reflect.TypeOf(HexInt64(0)), true 466 default: 467 return nil, false 468 } 469 } 470 471 func TestDiscriminator(t *testing.T) { 472 473 // Initialize the test case discriminator options to some defaults 474 // as long as the discriminator is not disabled with dd=true. 475 for i := range discriminatorTests { 476 tc := &discriminatorTests[i] 477 if !tc.dd { 478 if tc.tf == "" { 479 tc.tf = "_t" 480 } 481 if tc.vf == "" { 482 tc.vf = "_v" 483 } 484 } 485 } 486 487 t.Run("Encode", testDiscriminatorEncode) 488 t.Run("Decode", testDiscriminatorDecode) 489 } 490 491 func testDiscriminatorEncode(t *testing.T) { 492 for _, tc := range discriminatorTests { 493 tc := tc // capture the loop variable 494 t.Run("", func(t *testing.T) { 495 ee := tc.expEncErr 496 var w bytes.Buffer 497 498 enc := json.NewEncoder(&w) 499 enc.SetDiscriminator(tc.tf, tc.vf, tc.mode) 500 enc.SetTypeToDiscriminatorFunc(tc.discriminatorFunc) 501 502 if err := enc.Encode(tc.obj); err != nil { 503 if ee != err.Error() { 504 t.Errorf("expected error mismatch: e=%v, a=%v", ee, err) 505 } else if ee == "" { 506 t.Errorf("unexpected error: %v", err) 507 } 508 } else if ee != "" { 509 t.Errorf("expected error did not occur: %v", ee) 510 } else { 511 a, e := w.String(), tc.str 512 if tc.expStr != "" { 513 e = tc.expStr 514 } 515 if a != e+"\n" { 516 t.Errorf("mismatch: e=%s, a=%s", e, a) 517 } 518 } 519 }) 520 } 521 } 522 523 func testDiscriminatorDecode(t *testing.T) { 524 for _, tc := range discriminatorTests { 525 tc := tc // capture the loop variable 526 t.Run("", func(t *testing.T) { 527 ee := tc.expDecErr 528 dec := json.NewDecoder(strings.NewReader(tc.str)) 529 dec.SetDiscriminator(tc.tf, tc.vf, discriminatorToTypeFn) 530 531 var ( 532 err error 533 obj interface{} 534 ) 535 536 if tc.obj == nil || tc.mode&json.DiscriminatorEncodeTypeNameRootValue != 0 { 537 err = dec.Decode(&obj) 538 } else { 539 switch reflect.TypeOf(tc.obj).Name() { 540 case "DS1": 541 var o DS1 542 err = dec.Decode(&o) 543 obj = o 544 case "DS2": 545 var o DS2 546 err = dec.Decode(&o) 547 obj = o 548 case "DS3": 549 var o DS3 550 err = dec.Decode(&o) 551 obj = o 552 case "DS4": 553 var o DS4 554 err = dec.Decode(&o) 555 obj = o 556 case "DS5": 557 var o DS5 558 err = dec.Decode(&o) 559 obj = o 560 case "DS6": 561 var o DS6 562 err = dec.Decode(&o) 563 obj = o 564 case "DS7": 565 var o DS7 566 err = dec.Decode(&o) 567 obj = o 568 case "DS8": 569 var o DS8 570 err = dec.Decode(&o) 571 obj = o 572 case "DS9": 573 var o DS9 574 err = dec.Decode(&o) 575 obj = o 576 case "DS10": 577 var o DS10 578 o.DS9 = &DS9{} 579 err = dec.Decode(&o) 580 obj = o 581 default: 582 err = dec.Decode(&obj) 583 } 584 } 585 586 if err != nil { 587 if ee != err.Error() { 588 t.Errorf("expected error mismatch: e=%v, a=%v", ee, err) 589 } else if ee == "" { 590 t.Errorf("unexpected error: %v", err) 591 } 592 } else if ee != "" { 593 t.Errorf("expected error did not occur: %v", ee) 594 } else { 595 a, e := obj, tc.obj 596 if tc.expObj != nil { 597 e = tc.expObj 598 } 599 assertEqual(t, a, e) 600 } 601 }) 602 } 603 } 604 605 type emptyIface interface{} 606 607 type noop1 interface { 608 noop1() 609 } 610 611 type noop2 interface { 612 noop2() 613 } 614 615 type uintNoop uint 616 617 func (v uintNoop) noop1() {} 618 func (v *uintNoop) noop2() {} 619 620 type uint8Noop uint8 621 622 func (v uint8Noop) noop1() {} 623 func (v *uint8Noop) noop2() {} 624 625 type uint16Noop uint16 626 627 func (v uint16Noop) noop1() {} 628 func (v *uint16Noop) noop2() {} 629 630 type uint32Noop uint32 631 632 func (v uint32Noop) noop1() {} 633 func (v *uint32Noop) noop2() {} 634 635 type uint64Noop uint64 636 637 func (v uint64Noop) noop1() {} 638 func (v *uint64Noop) noop2() {} 639 640 type uintptrNoop uintptr 641 642 func (v uintptrNoop) noop1() {} 643 func (v *uintptrNoop) noop2() {} 644 645 type intNoop int 646 647 func (v intNoop) noop1() {} 648 func (v *intNoop) noop2() {} 649 650 type int8Noop int8 651 652 func (v int8Noop) noop1() {} 653 func (v *int8Noop) noop2() {} 654 655 type int16Noop int16 656 657 func (v int16Noop) noop1() {} 658 func (v *int16Noop) noop2() {} 659 660 type int32Noop int32 661 662 func (v int32Noop) noop1() {} 663 func (v *int32Noop) noop2() {} 664 665 type int64Noop int64 666 667 func (v int64Noop) noop1() {} 668 func (v *int64Noop) noop2() {} 669 670 type float32Noop float32 671 672 func (v float32Noop) noop1() {} 673 func (v *float32Noop) noop2() {} 674 675 type float64Noop float64 676 677 func (v float64Noop) noop1() {} 678 func (v *float64Noop) noop2() {} 679 680 type stringNoop string 681 682 func (v stringNoop) noop1() {} 683 func (v *stringNoop) noop2() {} 684 685 type boolNoop bool 686 687 func (v boolNoop) noop1() {} 688 func (v *boolNoop) noop2() {} 689 690 type mapStringIntNoop map[string]int 691 692 func (v mapStringIntNoop) noop1() {} 693 func (v *mapStringIntNoop) noop2() {} 694 695 type sliceIntNoop []int 696 697 func (v sliceIntNoop) noop1() {} 698 func (v *sliceIntNoop) noop2() {} 699 700 type arrayOfTwoIntsNoop [2]int 701 702 func (v arrayOfTwoIntsNoop) noop1() {} 703 func (v *arrayOfTwoIntsNoop) noop2() {} 704 705 func addrOfUint(v uint) *uint { 706 return &v 707 } 708 func addrOfUint8(v uint8) *uint8 { 709 return &v 710 } 711 func addrOfUint16(v uint16) *uint16 { 712 return &v 713 } 714 func addrOfUint32(v uint32) *uint32 { 715 return &v 716 } 717 func addrOfUint64(v uint64) *uint64 { 718 return &v 719 } 720 func addrOfUintptr(v uintptr) *uintptr { 721 return &v 722 } 723 724 func addrOfInt(v int) *int { 725 return &v 726 } 727 func addrOfInt8(v int8) *int8 { 728 return &v 729 } 730 func addrOfInt16(v int16) *int16 { 731 return &v 732 } 733 func addrOfInt32(v int32) *int32 { 734 return &v 735 } 736 func addrOfInt64(v int64) *int64 { 737 return &v 738 } 739 740 func addrOfFloat32(v float32) *float32 { 741 return &v 742 } 743 func addrOfFloat64(v float64) *float64 { 744 return &v 745 } 746 747 func addrOfBool(v bool) *bool { 748 return &v 749 } 750 func addrOfString(v string) *string { 751 return &v 752 } 753 754 func addrOfUintNoop(v uintNoop) *uintNoop { 755 return &v 756 } 757 func addrOfUint8Noop(v uint8Noop) *uint8Noop { 758 return &v 759 } 760 func addrOfUint16Noop(v uint16Noop) *uint16Noop { 761 return &v 762 } 763 func addrOfUint32Noop(v uint32Noop) *uint32Noop { 764 return &v 765 } 766 func addrOfUint64Noop(v uint64Noop) *uint64Noop { 767 return &v 768 } 769 func addrOfUintptrNoop(v uintptrNoop) *uintptrNoop { 770 return &v 771 } 772 773 func addrOfIntNoop(v intNoop) *intNoop { 774 return &v 775 } 776 func addrOfInt8Noop(v int8Noop) *int8Noop { 777 return &v 778 } 779 func addrOfInt16Noop(v int16Noop) *int16Noop { 780 return &v 781 } 782 func addrOfInt32Noop(v int32Noop) *int32Noop { 783 return &v 784 } 785 func addrOfInt64Noop(v int64Noop) *int64Noop { 786 return &v 787 } 788 789 func addrOfFloat32Noop(v float32Noop) *float32Noop { 790 return &v 791 } 792 func addrOfFloat64Noop(v float64Noop) *float64Noop { 793 return &v 794 } 795 796 func addrOfBoolNoop(v boolNoop) *boolNoop { 797 return &v 798 } 799 func addrOfStringNoop(v stringNoop) *stringNoop { 800 return &v 801 } 802 803 func addrOfMapStringIntNoop(v mapStringIntNoop) *mapStringIntNoop { 804 return &v 805 } 806 func addrOfSliceIntNoop(v sliceIntNoop) *sliceIntNoop { 807 return &v 808 } 809 func addrOfArrayOfTwoIntsNoop(v arrayOfTwoIntsNoop) *arrayOfTwoIntsNoop { 810 return &v 811 } 812 813 func assertEqual(t *testing.T, a, e interface{}) { 814 if !reflect.DeepEqual(a, e) { 815 t.Fatalf("Actual and expected values differ.\nactual: '%#v'\nexpected: '%#v'\n", a, e) 816 } 817 }