mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-26 21:17:23 +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)
|
return json.Unmarshal(data, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
nullBytes = []byte("null")
|
||||||
|
trueBytes = []byte("true")
|
||||||
|
falseBytes = []byte("false")
|
||||||
|
)
|
||||||
|
|
||||||
func toUnstructured(sv, dv reflect.Value) error {
|
func toUnstructured(sv, dv reflect.Value) error {
|
||||||
st, dt := sv.Type(), dv.Type()
|
st, dt := sv.Type(), dv.Type()
|
||||||
|
|
||||||
@ -435,33 +441,58 @@ func toUnstructured(sv, dv reflect.Value) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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.
|
// We're done - we don't need to store anything.
|
||||||
} else {
|
|
||||||
switch {
|
case bytes.Equal(data, trueBytes):
|
||||||
case len(data) > 0 && data[0] == '"':
|
dv.Set(reflect.ValueOf(true))
|
||||||
var result string
|
|
||||||
err := json.Unmarshal(data, &result)
|
case bytes.Equal(data, falseBytes):
|
||||||
if err != nil {
|
dv.Set(reflect.ValueOf(false))
|
||||||
return fmt.Errorf("error decoding from json: %v", err)
|
|
||||||
}
|
case data[0] == '"':
|
||||||
dv.Set(reflect.ValueOf(result))
|
var result string
|
||||||
case len(data) > 0 && data[0] == '{':
|
err := json.Unmarshal(data, &result)
|
||||||
result := make(map[string]interface{})
|
if err != nil {
|
||||||
err := json.Unmarshal(data, &result)
|
return fmt.Errorf("error decoding string from json: %v", err)
|
||||||
if err != nil {
|
}
|
||||||
return fmt.Errorf("error decoding from json: %v", err)
|
dv.Set(reflect.ValueOf(result))
|
||||||
}
|
|
||||||
dv.Set(reflect.ValueOf(result))
|
case data[0] == '{':
|
||||||
default:
|
result := make(map[string]interface{})
|
||||||
var result int64
|
err := json.Unmarshal(data, &result)
|
||||||
err := json.Unmarshal(data, &result)
|
if err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("error decoding object from json: %v", err)
|
||||||
return fmt.Errorf("error decoding from json: %v", err)
|
}
|
||||||
}
|
dv.Set(reflect.ValueOf(result))
|
||||||
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +71,18 @@ type F struct {
|
|||||||
I []float32 `json:"fi"`
|
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{}) {
|
func doRoundTrip(t *testing.T, item interface{}) {
|
||||||
data, err := json.Marshal(item)
|
data, err := json.Marshal(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -439,3 +451,36 @@ func TestFloatIntConversion(t *testing.T) {
|
|||||||
t.Errorf("Incorrect conversion, diff: %v", diff.ObjectReflectDiff(obj, unmarshalled))
|
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
|
// If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
|
||||||
return convertMapNumbers(*v)
|
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:
|
default:
|
||||||
return json.Unmarshal(data, v)
|
return json.Unmarshal(data, v)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user