diff --git a/decode.go b/decode.go index f3e583a0..bf177371 100644 --- a/decode.go +++ b/decode.go @@ -1489,3 +1489,25 @@ func (d *Decoder) DecodeContext(ctx context.Context, v interface{}) error { } return nil } + +// DecodeFromNode decodes node into the value pointed to by v. +func (d *Decoder) DecodeFromNode(node ast.Node, v interface{}) error { + return d.DecodeFromNodeContext(context.Background(), node, v) +} + +// DecodeFromNodeContext decodes node into the value pointed to by v with context.Context. +func (d *Decoder) DecodeFromNodeContext(ctx context.Context, node ast.Node, v interface{}) error { + rv := reflect.ValueOf(v) + if rv.Type().Kind() != reflect.Ptr { + return errors.ErrDecodeRequiredPointerType + } + if !d.isInitialized() { + if err := d.decodeInit(); err != nil { + return errors.Wrapf(err, "failed to decodInit") + } + } + if err := d.decodeValue(ctx, rv.Elem(), node); err != nil { + return errors.Wrapf(err, "failed to decode value") + } + return nil +} diff --git a/decode_test.go b/decode_test.go index 7d5960f3..79d7d49e 100644 --- a/decode_test.go +++ b/decode_test.go @@ -16,6 +16,7 @@ import ( "github.com/goccy/go-yaml" "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/internal/errors" "github.com/goccy/go-yaml/parser" "golang.org/x/xerrors" ) @@ -1716,6 +1717,40 @@ func Test_UnmarshalerWithContext(t *testing.T) { } } +func TestDecoder_DecodeFromNode(t *testing.T) { + t.Run("with reference", func(t *testing.T) { + anchor := strings.NewReader(` +map: &map + text: hello`) + var buf bytes.Buffer + dec := yaml.NewDecoder(&buf, yaml.ReferenceReaders(anchor)) + f, err := parser.ParseBytes([]byte("map: *map"), 0) + if err != nil { + t.Fatalf("failed to parse: %s", err) + } + type T struct { + Map map[string]string + } + var v T + if err := dec.DecodeFromNode(f.Docs[0].Body, &v); err != nil { + t.Fatalf("failed to decode: %s", err) + } + actual := fmt.Sprintf("%+v", v) + expect := fmt.Sprintf("%+v", T{map[string]string{"text": "hello"}}) + if actual != expect { + t.Fatalf("actual=[%s], expect=[%s]", actual, expect) + } + }) + t.Run("value is not pointer", func(t *testing.T) { + var buf bytes.Buffer + var v bool + err := yaml.NewDecoder(&buf).DecodeFromNode(nil, v) + if !xerrors.Is(err, errors.ErrDecodeRequiredPointerType) { + t.Fatalf("unexpected error: %s", err) + } + }) +} + func Example_JSONTags() { yml := `--- foo: 1 @@ -1756,6 +1791,22 @@ complecated: string // ^ } +func Example_Unmarshal_Node() { + f, err := parser.ParseBytes([]byte("text: node example"), 0) + if err != nil { + panic(err) + } + var v struct { + Text string `yaml:"text"` + } + if err := yaml.NodeToValue(f.Docs[0].Body, &v); err != nil { + panic(err) + } + fmt.Println(v.Text) + // OUTPUT: + // node example +} + type unmarshalableYAMLStringValue string func (v *unmarshalableYAMLStringValue) UnmarshalYAML(b []byte) error { diff --git a/yaml.go b/yaml.go index ad404cf5..2f626db4 100644 --- a/yaml.go +++ b/yaml.go @@ -197,6 +197,15 @@ func UnmarshalWithContext(ctx context.Context, data []byte, v interface{}, opts return nil } +// NodeToValue converts node to the value pointed to by v. +func NodeToValue(node ast.Node, v interface{}, opts ...DecodeOption) error { + var buf bytes.Buffer + if err := NewDecoder(&buf, opts...).DecodeFromNode(node, v); err != nil { + return errors.Wrapf(err, "failed to convert node to value") + } + return nil +} + // FormatError is a utility function that takes advantage of the metadata // stored in the errors returned by this package's parser. //