Add decode option that allows specific field prefixes (#795)

This would be paired with DisallowUnknownField where you generally want
to make sure the yaml does not have unknown fields, but allow for cases
where the yaml has some extra stuff in it, e.g. for custom metadata or
using fields as variables for anchors.

In our specific case we allow `x-*` fields for these cases.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
Brian Goff 2025-10-20 18:27:08 -07:00 committed by GitHub
parent 25e5d90942
commit 90e8525591
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 46 additions and 1 deletions

View file

@ -40,6 +40,7 @@ type Decoder struct {
isResolvedReference bool
validator StructValidator
disallowUnknownField bool
allowedFieldPrefixes []string
allowDuplicateMapKey bool
useOrderedMap bool
useJSONUnmarshaler bool
@ -1446,7 +1447,16 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
// Unknown fields are expected (they could be fields from the parent struct).
if len(unknownFields) != 0 && d.disallowUnknownField && src.GetToken() != nil {
for key, node := range unknownFields {
return errors.ErrUnknownField(fmt.Sprintf(`unknown field "%s"`, key), node.GetToken())
var ok bool
for _, prefix := range d.allowedFieldPrefixes {
if strings.HasPrefix(key, prefix) {
ok = true
break
}
}
if !ok {
return errors.ErrUnknownField(fmt.Sprintf(`unknown field "%s"`, key), node.GetToken())
}
}
}

View file

@ -1933,6 +1933,32 @@ children:
t.Fatalf(`v.Children[1].B should be 2, got %d`, v.Children[1].B)
}
})
t.Run("with allowed prefixes", func(t *testing.T) {
var v struct {
A string `yaml:"a"`
}
yml := `---
a: a_value
x-some-extra-thing: b_value
`
if err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowUnknownField(), yaml.AllowFieldPrefixes("x-")).Decode(&v); err != nil {
t.Fatalf(`parsing should succeed: %s`, err)
}
yml = `---
a: a_value
b: b_value
x-some-extra-thing: b_value
`
err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowUnknownField(), yaml.AllowFieldPrefixes("x-")).Decode(&v)
if err == nil {
t.Fatal("expected error")
}
})
}
func TestDecoder_AllowDuplicateMapKey(t *testing.T) {

View file

@ -69,6 +69,15 @@ func DisallowUnknownField() DecodeOption {
}
}
// AllowFieldPrefixes, when paired with [DisallowUnknownField], allows fields
// with the specified prefixes to bypass the unknown field check.
func AllowFieldPrefixes(prefixes ...string) DecodeOption {
return func(d *Decoder) error {
d.allowedFieldPrefixes = append(d.allowedFieldPrefixes, prefixes...)
return nil
}
}
// AllowDuplicateMapKey ignore syntax error when mapping keys that are duplicates.
func AllowDuplicateMapKey() DecodeOption {
return func(d *Decoder) error {