diff --git a/decode.go b/decode.go index 042e0257..20bf4cfb 100644 --- a/decode.go +++ b/decode.go @@ -497,6 +497,9 @@ func (d *Decoder) createDecodedNewValue(typ reflect.Type, node ast.Node) (reflec return newValue, nil } } + if node.Type() == ast.NullType { + return reflect.Zero(typ), nil + } newValue := d.createDecodableValue(typ) if err := d.decodeValue(newValue, node); err != nil { return newValue, errors.Wrapf(err, "failed to decode value") diff --git a/decode_test.go b/decode_test.go index c016be63..7547cf7c 100644 --- a/decode_test.go +++ b/decode_test.go @@ -1401,6 +1401,34 @@ func TestUnmarshalableString(t *testing.T) { }) } +type unmarshalablePtrStringContainer struct { + V *string `yaml:"value" json:"value"` +} + +func TestUnmarshalablePtrString(t *testing.T) { + t.Run("empty string", func(t *testing.T) { + t.Parallel() + var container unmarshalablePtrStringContainer + if err := yaml.Unmarshal([]byte(`value: ""`), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if *container.V != "" { + t.Fatalf("expected empty string, but %q is set", *container.V) + } + }) + + t.Run("null", func(t *testing.T) { + t.Parallel() + var container unmarshalablePtrStringContainer + if err := yaml.Unmarshal([]byte(`value: null`), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if container.V != (*string)(nil) { + t.Fatalf("expected nil, but %q is set", *container.V) + } + }) +} + type unmarshalableIntValue int func (v *unmarshalableIntValue) UnmarshalYAML(raw []byte) error { @@ -1448,3 +1476,31 @@ func TestUnmarshalableInt(t *testing.T) { } }) } + +type unmarshalablePtrIntContainer struct { + V *int `yaml:"value" json:"value"` +} + +func TestUnmarshalablePtrInt(t *testing.T) { + t.Run("empty int", func(t *testing.T) { + t.Parallel() + var container unmarshalablePtrIntContainer + if err := yaml.Unmarshal([]byte(`value: 0`), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if *container.V != 0 { + t.Fatalf("expected 0, but %q is set", *container.V) + } + }) + + t.Run("null", func(t *testing.T) { + t.Parallel() + var container unmarshalablePtrIntContainer + if err := yaml.Unmarshal([]byte(`value: null`), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if container.V != (*int)(nil) { + t.Fatalf("expected nil, but %q is set", *container.V) + } + }) +} diff --git a/encode_test.go b/encode_test.go index 727b31df..09cb2c5e 100644 --- a/encode_test.go +++ b/encode_test.go @@ -12,6 +12,9 @@ import ( "github.com/goccy/go-yaml/ast" ) +var zero = 0 +var emptyStr = "" + func TestEncoder(t *testing.T) { tests := []struct { source string @@ -249,6 +252,46 @@ func TestEncoder(t *testing.T) { 1, 0, }, }, + { + "a: \"\"\n", + struct { + A string + }{ + "", + }, + }, + { + "a: null\n", + struct { + A *string + }{ + nil, + }, + }, + { + "a: \"\"\n", + struct { + A *string + }{ + &emptyStr, + }, + }, + { + "a: null\n", + struct { + A *int + }{ + nil, + }, + }, + { + "a: 0\n", + struct { + A *int + }{ + &zero, + }, + }, // Conditional flag {