Skip to content
This repository has been archived by the owner on Aug 16, 2024. It is now read-only.

Commit

Permalink
Add UnknownFieldHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
blaubaer committed Aug 15, 2024
1 parent 4fce4f9 commit b665f11
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 31 deletions.
33 changes: 19 additions & 14 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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")
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -325,7 +327,7 @@ type decoder struct {
stringMapType reflect.Type
generalMapType reflect.Type

knownFields bool
settings *settings
uniqueKeys bool
decodeCount int
aliasCount int
Expand All @@ -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
Expand Down Expand Up @@ -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())
}
}
}

Expand Down
1 change: 1 addition & 0 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
58 changes: 41 additions & 17 deletions yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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 {
Expand All @@ -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() {
Expand All @@ -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()
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
}

0 comments on commit b665f11

Please sign in to comment.