From b665f1199a3a70c757f843415f6de02e8b87e9e2 Mon Sep 17 00:00:00 2001 From: Gregor Noczinski Date: Thu, 15 Aug 2024 23:20:41 +0200 Subject: [PATCH] Add `UnknownFieldHandler` --- decode.go | 33 ++++++++++++++++------------ decode_test.go | 1 + yaml.go | 58 +++++++++++++++++++++++++++++++++++--------------- 3 files changed, 61 insertions(+), 31 deletions(-) diff --git a/decode.go b/decode.go index 91b6d3c4..b72a4e9a 100644 --- a/decode.go +++ b/decode.go @@ -37,12 +37,12 @@ type parser struct { anchors map[string]*Node doneInit bool textless bool - decoder *Decoder + settings *settings } -func newParser(b []byte, dec *Decoder) *parser { +func newParser(b []byte, s *settings) *parser { p := parser{ - decoder: dec, + settings: s, } if !yaml_parser_initialize(&p.parser) { panic("failed to initialize YAML emitter") @@ -54,9 +54,9 @@ func newParser(b []byte, dec *Decoder) *parser { return &p } -func newParserFromReader(r io.Reader, dec *Decoder) *parser { +func newParserFromReader(r io.Reader, s *settings) *parser { p := parser{ - decoder: dec, + settings: s, } if !yaml_parser_initialize(&p.parser) { panic("failed to initialize YAML emitter") @@ -184,11 +184,13 @@ func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { tag, _ = resolve("", value) } n := &Node{ - Kind: kind, - Tag: tag, - Value: value, - Style: style, - decoder: p.decoder, + Kind: kind, + Tag: tag, + Value: value, + Style: style, + } + if p.settings != nil { + n.settings = *p.settings } if !p.textless { n.Line = p.event.start_mark.line + 1 @@ -325,7 +327,7 @@ type decoder struct { stringMapType reflect.Type generalMapType reflect.Type - knownFields bool + settings *settings uniqueKeys bool decodeCount int aliasCount int @@ -344,11 +346,12 @@ var ( ptrTimeType = reflect.TypeOf(&time.Time{}) ) -func newDecoder() *decoder { +func newDecoder(s *settings) *decoder { d := &decoder{ stringMapType: stringMapType, generalMapType: generalMapType, uniqueKeys: true, + settings: s, } d.aliases = make(map[*Node]bool) return d @@ -947,8 +950,10 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { value := reflect.New(elemType).Elem() d.unmarshal(n.Content[i+1], value) inlineMap.SetMapIndex(name, value) - } else if d.knownFields { - d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) + } else if f := d.settings.unknownField; f != nil { + if err := f(ni, name, out); err != nil { + d.terrors = append(d.terrors, err.Error()) + } } } diff --git a/decode_test.go b/decode_test.go index 77654e09..83f6fa51 100644 --- a/decode_test.go +++ b/decode_test.go @@ -1703,6 +1703,7 @@ func (s *S) TestUnmarshalKnownFields(c *C) { t := reflect.ValueOf(item.value).Type() value := reflect.New(t) dec := yaml.NewDecoder(bytes.NewBuffer([]byte(item.data))) + //goland:noinspection GoDeprecation dec.KnownFields(item.known) err := dec.Decode(value.Interface()) c.Assert(err, ErrorMatches, item.error) diff --git a/yaml.go b/yaml.go index 2cd47fd9..88a6747b 100644 --- a/yaml.go +++ b/yaml.go @@ -85,13 +85,25 @@ type Marshaler interface { // See the documentation of Marshal for the format of tags and a list of // supported tag options. func Unmarshal(in []byte, out interface{}) (err error) { - return unmarshal(in, out, false) + return unmarshal(in, out, &settings{}) +} + +// UnknownFieldHandler is called if an unknown field was found. +// This handler can decide what to do in this situation. If it does not +// return any error this situation will be ignored. +type UnknownFieldHandler func(node *Node, name reflect.Value, out reflect.Value) error + +// DefaultUnknownFieldHandler is the default implementation of UnknownFieldHandler. +// +// See UnknownFieldHandler for more details. +func DefaultUnknownFieldHandler(node *Node, name reflect.Value, out reflect.Value) error { + return fmt.Errorf("line %d: field %s not found in type %s", node.Line, name.String(), out.Type()) } // A Decoder reads and decodes YAML values from an input stream. type Decoder struct { - parser *parser - knownFields bool + settings + parser *parser } // NewDecoder returns a new decoder that reads from r. @@ -100,16 +112,22 @@ type Decoder struct { // data from r beyond the YAML values requested. func NewDecoder(r io.Reader) *Decoder { var dec Decoder - p := newParserFromReader(r, &dec) - p.decoder = &dec - dec.parser = p + dec.parser = newParserFromReader(r, &dec.settings) return &dec } // KnownFields ensures that the keys in decoded mappings to // exist as fields in the struct being decoded into. +// +// Deprecated: OnUnknownField(DefaultUnknownFieldHandler or nil) should be used instead. +// +//goland:noinspection GoDeprecation func (dec *Decoder) KnownFields(enable bool) { - dec.knownFields = enable + if enable { + dec.unknownField = DefaultUnknownFieldHandler + } else { + dec.unknownField = nil + } } // Decode reads the next YAML-encoded value from its input @@ -118,8 +136,7 @@ func (dec *Decoder) KnownFields(enable bool) { // See the documentation for Unmarshal for details about the // conversion of YAML into a Go value. func (dec *Decoder) Decode(v interface{}) (err error) { - d := newDecoder() - d.knownFields = dec.knownFields + d := newDecoder(&dec.settings) defer handleErr(&err) node := dec.parser.parse() if node == nil { @@ -141,10 +158,7 @@ func (dec *Decoder) Decode(v interface{}) (err error) { // See the documentation for Unmarshal for details about the // conversion of YAML into a Go value. func (n *Node) Decode(v interface{}) (err error) { - d := newDecoder() - if n.decoder != nil { - d.knownFields = n.decoder.knownFields - } + d := newDecoder(&n.settings) defer handleErr(&err) out := reflect.ValueOf(v) if out.Kind() == reflect.Ptr && !out.IsNil() { @@ -157,9 +171,9 @@ func (n *Node) Decode(v interface{}) (err error) { return nil } -func unmarshal(in []byte, out interface{}, strict bool) (err error) { +func unmarshal(in []byte, out interface{}, s *settings) (err error) { defer handleErr(&err) - d := newDecoder() + d := newDecoder(s) p := newParser(in, nil) defer p.destroy() node := p.parse() @@ -265,7 +279,7 @@ func (n *Node) Encode(v interface{}) (err error) { defer e.destroy() e.marshalDoc("", reflect.ValueOf(v)) e.finish() - p := newParser(e.out, n.decoder) + p := newParser(e.out, &n.settings) p.textless = true defer p.destroy() doc := p.parse() @@ -416,7 +430,7 @@ type Node struct { Line int Column int - decoder *Decoder + settings } // IsZero returns whether the node has all of its fields unset. @@ -699,3 +713,13 @@ func isZero(v reflect.Value) bool { } return false } + +type settings struct { + unknownField UnknownFieldHandler +} + +// OnUnknownField is called when an unknown field was found. +// See UnknownFieldHandler for more details. +func (dec *settings) OnUnknownField(v UnknownFieldHandler) { + dec.unknownField = v +}