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

Add UnknownFieldHandler #3

Merged
merged 1 commit into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
Loading