github.com/vmware/govmomi@v0.37.2/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  }