Repository: goccy/go-yaml (go)

Issue:
bugs in yamlpath when looking for "internal" fields (with reproducible steps)
Given the following yaml
```yaml
s:
  - a:
      b: u1
      c: get1
      d: i1
  - w:
      c: bad
      e:
        - a:
            b: u2
            c: get2
            d: i2
```

Looking for every `c` that is a direct child of `a`, YamlPath should be `..a.c`:
```go
yml := `
s:
  - a:
      b: u1
      c: get1
      d: i1
  - w:
      c: bad
      e:
        - a:
            b: u2
            c: get2
            d: i2
`
		
path, _ := yaml.PathString("$..a.c")
n, _ := path.ReadNode(strings.NewReader(yml))
fmt.Println(n.String())

// Outputs:
// - b: u1
//   c: get1
//   d: i1
// - b: u2
//   c: get2
//   d: i2
```

It got the right top-level elements, but didn't drill into the `c` part.

Without the "internal" field, it works fine, but gives back nodes that are out of context (since its not defined in the path):
```go
path, _ := yaml.PathString("$..c")
n, _ := path.ReadNode(strings.NewReader(yml))
fmt.Println(n.String())

// Outputs:
// - get1
// - bad
// - get2
```

Further looking into the path returned by the node for one of the above resulting nodes we get: `$.s[0].a.c`. Generalized it to be `$.s[*].a.c`, and plugged it in the previous example, to now get a panic when trying to print the node:
```
github.com/goccy/go-yaml/ast.(*SequenceNode).blockStyleString(0x140005ae0a0)
        /Users/shlomi/go/pkg/mod/github.com/goccy/go-yaml@v1.9.5/ast/ast.go:1585 +0x1c8
github.com/goccy/go-yaml/ast.(*SequenceNode).String(0x140005ae0a0)
        /Users/shlomi/go/pkg/mod/github.com/goccy/go-yaml@v1.9.5/ast/ast.go:1619 +0x5c
github.com/this-example({0x16d7e378d, 0x6})
        example-above:Line-of-n.String() +0x14c
main.main()
```

However, trying to use the same with https://github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath, yields the correct answer:
```golang
import "github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
import "gopkg.in/yaml.v3"

var nn yaml.Node
err := yaml.Unmarshal([]byte(yml), &nn)

p, err := yamlpath.NewPath("$..a.c")
r, err := p.Find(&nn)
for _, node := range r {
	fmt.Println(node.Value)
}

// Outputs:
// get1
// get2
```

Pull request:
issue-281: add tests and fix panic for *
Before submitting your PR, please confirm the following.

partially resolves #281 

- added tests
- fixed null pointer panic with nill guard
- while the incorrect behaviour for some paths lookup remains unfixed, a test has been added to reproduce and highlight this issue.

- [X] Describe the purpose for which you created this PR.  
- [X] Create test code that corresponds to the modification

Patch:
diff --git a/ast/ast.go b/ast/ast.go
index c9bbf9d..50428a2 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -1598,6 +1598,9 @@ func (n *SequenceNode) blockStyleString() string {
 	}
 
 	for idx, value := range n.Values {
+		if value == nil {
+			continue
+		}
 		valueStr := value.String()
 		newLinePrefix := ""
 		if strings.HasPrefix(valueStr, "\n") {


Test patch:
diff --git a/path_test.go b/path_test.go
index 5b62d26..efdee3d 100644
--- a/path_test.go
+++ b/path_test.go
@@ -84,6 +84,36 @@ store:
 			path:     builder().Root().Child("store").Child("book").IndexAll().Child("author").Build(),
 			expected: []interface{}{"john", "ken"},
 		},
+		{
+			name: "$.store.book[*]",
+			path: builder().Root().Child("store").Child("book").IndexAll().Build(),
+			expected: []interface{}{
+				map[string]interface{}{
+					"author": "john",
+					"price":  uint64(10),
+				},
+				map[string]interface{}{
+					"author": "ken",
+					"price":  uint64(12),
+				},
+			},
+		},
+		{
+			name: "$..book[*]",
+			path: builder().Root().Recursive("book").IndexAll().Build(),
+			expected: []interface{}{
+				[]interface{}{
+					map[string]interface{}{
+						"author": "john",
+						"price":  uint64(10),
+					},
+					map[string]interface{}{
+						"author": "ken",
+						"price":  uint64(12),
+					},
+				},
+			},
+		},
 		{
 			name:     "$.store.book[0]",
 			path:     builder().Root().Child("store").Child("book").Index(0).Build(),
@@ -305,6 +335,143 @@ func TestPath_Invalid(t *testing.T) {
 	}
 }
 
+func TestPath_ReadNode(t *testing.T) {
+	tests := []struct {
+		name     string
+		path     string
+		src      string
+		expected interface{}
+	}{
+		{
+			name: "nested array sequence",
+			path: `$.a.b[0].c`,
+			src: `
+a:
+  b:
+   - c: 123
+  e: |
+   Line1
+   Line2
+`,
+			expected: uint64(123),
+		},
+		{
+			name: "nested array sequence issue#281",
+			path: `$..a.c`,
+			src: `
+s:
+  - a:
+      b: u1
+      c: get1
+      d: i1
+  - w:
+      c: bad
+      e:
+        - a:
+           b: u2
+           c: get2
+           d: i2
+`,
+			// The expected values are
+			// - get1
+			// - get2
+			expected: []interface{}{
+				map[string]interface{}{
+					"b": "u1",
+					"c": "get1",
+					"d": "i1",
+				},
+				map[string]interface{}{
+					"b": "u2",
+					"c": "get2",
+					"d": "i2",
+				},
+			},
+		},
+		{
+			name: "nested array sequence issue#281",
+			path: `$..c`,
+			src: `
+s:
+  - a:
+      b: u1
+      c: get1
+      d: i1
+  - w:
+      c: bad
+      e:
+        - a:
+            b: u2
+            c: get2
+            d: i2
+`,
+			expected: []interface{}{"get1", "bad", "get2"},
+		},
+		{
+			name: "nested array sequence issue#281",
+			path: `$.s[0].a.c`,
+			src: `
+s:
+  - a:
+      b: u1
+      c: get1
+      d: i1
+  - w:
+      c: bad
+      e:
+        - a:
+            b: u2
+            c: get2
+            d: i2
+`,
+			expected: "get1",
+		},
+		{
+			name: "nested array sequence issue#281",
+			path: "$.s[*].a.c",
+			src: `
+s:
+  - a:
+      b: u1
+      c: get1
+      d: i1
+  - w:
+      c: bad
+      e:
+        - a:
+            b: u2
+            c: get2
+            d: i2
+`,
+			expected: []interface{}{"get1"},
+		},
+	}
+	for _, test := range tests {
+		path, err := yaml.PathString(test.path)
+		if err != nil {
+			t.Fatal(err)
+		}
+		t.Run(fmt.Sprintf("path.ReadNode %s path %s", test.name, test.path), func(t *testing.T) {
+			file, err := parser.ParseBytes([]byte(test.src), 0)
+			if err != nil {
+				t.Fatal(err)
+			}
+			n, err := path.ReadNode(file)
+			if err != nil {
+				t.Fatal("expected error", err)
+			}
+			var v interface{}
+			err = yaml.Unmarshal([]byte(n.String()), &v)
+			if err != nil {
+				t.Fatal(err)
+			}
+			if !reflect.DeepEqual(test.expected, v) {
+				t.Fatalf("expected %v(%T) but got %v(%T)", test.expected, test.expected, v, v)
+			}
+		})
+	}
+}
+
 func TestPath_Merge(t *testing.T) {
 	tests := []struct {
 		path     string