Repository: goccy/go-yaml
Pull request #83 (2020-02-22 03:02:58)

Distinguish between empty ptr value (e.g. *int, *string) and nil ptr value
Hi @goccy .

I want to decode "empty ptr value" and "nil ptr value" **like go-yaml/yaml**.

| | go-yaml/yaml | encoding/json | goccy/go-yaml v1.3.1 | 
| --- | --- | --- | --- |
| empty ptr string | `""` | `""` | `""` | 
| nil ptr string | `nil` | `nil` | `""` | 
| play.golang.org | [here](https://play.golang.org/p/lok0fbZhoUV) | [here](https://play.golang.org/p/2a4_ymoqQ9Z) | [here](https://play.golang.org/p/Uw9_lC2HIdx) | 

This library is used by my tool ( THANK YOU !! ). [I am applying a workaround for the above detection](https://github.com/k1LoW/tbls/blob/15b1374f0283dfa4cc2f4ca5038d51ba3dccdd2f/schema/yaml.go#L100-L111).


Patch:
diff --git a/decode.go b/decode.go
index 042e025..20bf4cf 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")


Test patch:
diff --git a/decode_test.go b/decode_test.go
index c016be6..7547cf7 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 727b31d..09cb2c5 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
 		{