mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 12:15:52 +00:00
Merge pull request #49407 from liggitt/unstructured-marshaler
Automatic merge from submit-queue (batch tested with PRs 45345, 49470, 49407, 49448, 49486) Fix unstructured marshaler to handle all JSON types Split from https://github.com/kubernetes/kubernetes/pull/45294 /assign @wojtek-t
This commit is contained in:
commit
17b9c21a6a
@ -420,6 +420,12 @@ func toUnstructuredViaJSON(obj interface{}, u *map[string]interface{}) error {
|
||||
return json.Unmarshal(data, u)
|
||||
}
|
||||
|
||||
var (
|
||||
nullBytes = []byte("null")
|
||||
trueBytes = []byte("true")
|
||||
falseBytes = []byte("false")
|
||||
)
|
||||
|
||||
func toUnstructured(sv, dv reflect.Value) error {
|
||||
st, dt := sv.Type(), dv.Type()
|
||||
|
||||
@ -435,33 +441,58 @@ func toUnstructured(sv, dv reflect.Value) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bytes.Equal(data, []byte("null")) {
|
||||
switch {
|
||||
case len(data) == 0:
|
||||
return fmt.Errorf("error decoding from json: empty value")
|
||||
|
||||
case bytes.Equal(data, nullBytes):
|
||||
// We're done - we don't need to store anything.
|
||||
} else {
|
||||
switch {
|
||||
case len(data) > 0 && data[0] == '"':
|
||||
var result string
|
||||
err := json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding from json: %v", err)
|
||||
}
|
||||
dv.Set(reflect.ValueOf(result))
|
||||
case len(data) > 0 && data[0] == '{':
|
||||
result := make(map[string]interface{})
|
||||
err := json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding from json: %v", err)
|
||||
}
|
||||
dv.Set(reflect.ValueOf(result))
|
||||
default:
|
||||
var result int64
|
||||
err := json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding from json: %v", err)
|
||||
}
|
||||
dv.Set(reflect.ValueOf(result))
|
||||
|
||||
case bytes.Equal(data, trueBytes):
|
||||
dv.Set(reflect.ValueOf(true))
|
||||
|
||||
case bytes.Equal(data, falseBytes):
|
||||
dv.Set(reflect.ValueOf(false))
|
||||
|
||||
case data[0] == '"':
|
||||
var result string
|
||||
err := json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding string from json: %v", err)
|
||||
}
|
||||
dv.Set(reflect.ValueOf(result))
|
||||
|
||||
case data[0] == '{':
|
||||
result := make(map[string]interface{})
|
||||
err := json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding object from json: %v", err)
|
||||
}
|
||||
dv.Set(reflect.ValueOf(result))
|
||||
|
||||
case data[0] == '[':
|
||||
result := make([]interface{}, 0)
|
||||
err := json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding array from json: %v", err)
|
||||
}
|
||||
dv.Set(reflect.ValueOf(result))
|
||||
|
||||
default:
|
||||
var (
|
||||
resultInt int64
|
||||
resultFloat float64
|
||||
err error
|
||||
)
|
||||
if err = json.Unmarshal(data, &resultInt); err == nil {
|
||||
dv.Set(reflect.ValueOf(resultInt))
|
||||
} else if err = json.Unmarshal(data, &resultFloat); err == nil {
|
||||
dv.Set(reflect.ValueOf(resultFloat))
|
||||
} else {
|
||||
return fmt.Errorf("error decoding number from json: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,18 @@ type F struct {
|
||||
I []float32 `json:"fi"`
|
||||
}
|
||||
|
||||
type G struct {
|
||||
Custom Custom `json:"custom"`
|
||||
}
|
||||
|
||||
type Custom struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (c Custom) MarshalJSON() ([]byte, error) {
|
||||
return c.data, nil
|
||||
}
|
||||
|
||||
func doRoundTrip(t *testing.T, item interface{}) {
|
||||
data, err := json.Marshal(item)
|
||||
if err != nil {
|
||||
@ -439,3 +451,36 @@ func TestFloatIntConversion(t *testing.T) {
|
||||
t.Errorf("Incorrect conversion, diff: %v", diff.ObjectReflectDiff(obj, unmarshalled))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomToUnstructured(t *testing.T) {
|
||||
testcases := []struct {
|
||||
Data string
|
||||
Expected interface{}
|
||||
}{
|
||||
{Data: `null`, Expected: nil},
|
||||
{Data: `true`, Expected: true},
|
||||
{Data: `false`, Expected: false},
|
||||
{Data: `[]`, Expected: []interface{}{}},
|
||||
{Data: `[1]`, Expected: []interface{}{int64(1)}},
|
||||
{Data: `{}`, Expected: map[string]interface{}{}},
|
||||
{Data: `{"a":1}`, Expected: map[string]interface{}{"a": int64(1)}},
|
||||
{Data: `0`, Expected: int64(0)},
|
||||
{Data: `0.0`, Expected: float64(0)},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
result, err := DefaultConverter.ToUnstructured(&G{Custom: Custom{data: []byte(tc.Data)}})
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", tc.Data, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fieldResult := result["custom"]
|
||||
if !reflect.DeepEqual(fieldResult, tc.Expected) {
|
||||
t.Errorf("%s: expected %v, got %v", tc.Data, tc.Expected, fieldResult)
|
||||
// t.Log("expected", spew.Sdump(tc.Expected))
|
||||
// t.Log("actual", spew.Sdump(fieldResult))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,18 @@ func Unmarshal(data []byte, v interface{}) error {
|
||||
// If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
|
||||
return convertMapNumbers(*v)
|
||||
|
||||
case *[]interface{}:
|
||||
// Build a decoder from the given data
|
||||
decoder := json.NewDecoder(bytes.NewBuffer(data))
|
||||
// Preserve numbers, rather than casting to float64 automatically
|
||||
decoder.UseNumber()
|
||||
// Run the decode
|
||||
if err := decoder.Decode(v); err != nil {
|
||||
return err
|
||||
}
|
||||
// If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
|
||||
return convertSliceNumbers(*v)
|
||||
|
||||
default:
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user