Support DisallowUnknownFields

This commit is contained in:
Masaaki Goshima 2020-08-11 19:05:20 +09:00
parent 63940902c0
commit e112aa753e
16 changed files with 80 additions and 6 deletions

View File

@ -18,10 +18,12 @@ func (d Delim) String() string {
type decoder interface { type decoder interface {
decode([]byte, int64, uintptr) (int64, error) decode([]byte, int64, uintptr) (int64, error)
decodeStream(*stream, uintptr) error decodeStream(*stream, uintptr) error
setDisallowUnknownFields(bool)
} }
type Decoder struct { type Decoder struct {
s *stream s *stream
disallowUnknownFields bool
} }
type decoderMap struct { type decoderMap struct {
@ -97,6 +99,7 @@ func (d *Decoder) decode(src []byte, header *interfaceHeader) error {
cachedDecoder.set(typeptr, compiledDec) cachedDecoder.set(typeptr, compiledDec)
dec = compiledDec dec = compiledDec
} }
dec.setDisallowUnknownFields(d.disallowUnknownFields)
if _, err := dec.decode(src, 0, ptr); err != nil { if _, err := dec.decode(src, 0, ptr); err != nil {
return err return err
} }
@ -161,6 +164,7 @@ func (d *Decoder) Decode(v interface{}) error {
cachedDecoder.set(typeptr, compiledDec) cachedDecoder.set(typeptr, compiledDec)
dec = compiledDec dec = compiledDec
} }
dec.setDisallowUnknownFields(d.disallowUnknownFields)
if err := d.prepareForDecode(); err != nil { if err := d.prepareForDecode(); err != nil {
return err return err
} }
@ -248,7 +252,7 @@ func (d *Decoder) Token() (Token, error) {
// is a struct and the input contains object keys which do not match any // is a struct and the input contains object keys which do not match any
// non-ignored, exported fields in the destination. // non-ignored, exported fields in the destination.
func (d *Decoder) DisallowUnknownFields() { func (d *Decoder) DisallowUnknownFields() {
d.disallowUnknownFields = true
} }
func (d *Decoder) InputOffset() int64 { func (d *Decoder) InputOffset() int64 {

View File

@ -16,6 +16,10 @@ func newArrayDecoder(dec decoder, elemType *rtype, alen int) *arrayDecoder {
} }
} }
func (d *arrayDecoder) setDisallowUnknownFields(disallowUnknownFields bool) {
d.valueDecoder.setDisallowUnknownFields(disallowUnknownFields)
}
func (d *arrayDecoder) decodeStream(s *stream, p uintptr) error { func (d *arrayDecoder) decodeStream(s *stream, p uintptr) error {
for { for {
switch s.char() { switch s.char() {

View File

@ -58,6 +58,8 @@ func falseBytes(s *stream) error {
return nil return nil
} }
func (d *boolDecoder) setDisallowUnknownFields(_ bool) {}
func (d *boolDecoder) decodeStream(s *stream, p uintptr) error { func (d *boolDecoder) decodeStream(s *stream, p uintptr) error {
s.skipWhiteSpace() s.skipWhiteSpace()
for { for {

View File

@ -46,6 +46,8 @@ func floatBytes(s *stream) []byte {
return s.buf[start:s.cursor] return s.buf[start:s.cursor]
} }
func (d *floatDecoder) setDisallowUnknownFields(_ bool) {}
func (d *floatDecoder) decodeStreamByte(s *stream) ([]byte, error) { func (d *floatDecoder) decodeStreamByte(s *stream) ([]byte, error) {
for { for {
switch s.char() { switch s.char() {

View File

@ -49,6 +49,8 @@ var (
} }
) )
func (d *intDecoder) setDisallowUnknownFields(_ bool) {}
func (d *intDecoder) decodeStreamByte(s *stream) ([]byte, error) { func (d *intDecoder) decodeStreamByte(s *stream) ([]byte, error) {
for { for {
switch s.char() { switch s.char() {

View File

@ -8,6 +8,7 @@ import (
type interfaceDecoder struct { type interfaceDecoder struct {
typ *rtype typ *rtype
dummy unsafe.Pointer // for escape value dummy unsafe.Pointer // for escape value
disallowUnknownFields bool
} }
func newInterfaceDecoder(typ *rtype) *interfaceDecoder { func newInterfaceDecoder(typ *rtype) *interfaceDecoder {
@ -16,6 +17,10 @@ func newInterfaceDecoder(typ *rtype) *interfaceDecoder {
} }
} }
func (d *interfaceDecoder) setDisallowUnknownFields(disallowUnknownFields bool) {
d.disallowUnknownFields = disallowUnknownFields
}
func (d *interfaceDecoder) numDecoder(s *stream) decoder { func (d *interfaceDecoder) numDecoder(s *stream) decoder {
if s.useNumber { if s.useNumber {
return newNumberDecoder(func(p uintptr, v Number) { return newNumberDecoder(func(p uintptr, v Number) {
@ -46,6 +51,7 @@ func (d *interfaceDecoder) decodeStream(s *stream, p uintptr) error {
newInterfaceDecoder(d.typ), newInterfaceDecoder(d.typ),
newInterfaceDecoder(d.typ), newInterfaceDecoder(d.typ),
) )
dec.setDisallowUnknownFields(d.disallowUnknownFields)
if err := dec.decodeStream(s, uintptr(ptr)); err != nil { if err := dec.decodeStream(s, uintptr(ptr)); err != nil {
return err return err
} }
@ -60,6 +66,7 @@ func (d *interfaceDecoder) decodeStream(s *stream, p uintptr) error {
d.typ, d.typ,
d.typ.Size(), d.typ.Size(),
) )
dec.setDisallowUnknownFields(d.disallowUnknownFields)
if err := dec.decodeStream(s, uintptr(ptr)); err != nil { if err := dec.decodeStream(s, uintptr(ptr)); err != nil {
return err return err
} }
@ -128,6 +135,7 @@ func (d *interfaceDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, e
newInterfaceDecoder(d.typ), newInterfaceDecoder(d.typ),
newInterfaceDecoder(d.typ), newInterfaceDecoder(d.typ),
) )
dec.setDisallowUnknownFields(d.disallowUnknownFields)
cursor, err := dec.decode(buf, cursor, uintptr(ptr)) cursor, err := dec.decode(buf, cursor, uintptr(ptr))
if err != nil { if err != nil {
return 0, err return 0, err
@ -143,6 +151,7 @@ func (d *interfaceDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, e
d.typ, d.typ,
d.typ.Size(), d.typ.Size(),
) )
dec.setDisallowUnknownFields(d.disallowUnknownFields)
cursor, err := dec.decode(buf, cursor, uintptr(ptr)) cursor, err := dec.decode(buf, cursor, uintptr(ptr))
if err != nil { if err != nil {
return 0, err return 0, err

View File

@ -26,6 +26,11 @@ func makemap(*rtype, int) unsafe.Pointer
//go:noescape //go:noescape
func mapassign(t *rtype, m unsafe.Pointer, key, val unsafe.Pointer) func mapassign(t *rtype, m unsafe.Pointer, key, val unsafe.Pointer)
func (d *mapDecoder) setDisallowUnknownFields(disallowUnknownFields bool) {
d.keyDecoder.setDisallowUnknownFields(disallowUnknownFields)
d.valueDecoder.setDisallowUnknownFields(disallowUnknownFields)
}
func (d *mapDecoder) setKey(buf []byte, cursor int64, key interface{}) (int64, error) { func (d *mapDecoder) setKey(buf []byte, cursor int64, key interface{}) (int64, error) {
header := (*interfaceHeader)(unsafe.Pointer(&key)) header := (*interfaceHeader)(unsafe.Pointer(&key))
return d.keyDecoder.decode(buf, cursor, uintptr(header.ptr)) return d.keyDecoder.decode(buf, cursor, uintptr(header.ptr))

View File

@ -16,6 +16,8 @@ func newNumberDecoder(op func(uintptr, Number)) *numberDecoder {
} }
} }
func (d *numberDecoder) setDisallowUnknownFields(_ bool) {}
func (d *numberDecoder) decodeStream(s *stream, p uintptr) error { func (d *numberDecoder) decodeStream(s *stream, p uintptr) error {
bytes, err := d.floatDecoder.decodeStreamByte(s) bytes, err := d.floatDecoder.decodeStreamByte(s)
if err != nil { if err != nil {

View File

@ -16,6 +16,10 @@ func newPtrDecoder(dec decoder, typ *rtype) *ptrDecoder {
//go:linkname unsafe_New reflect.unsafe_New //go:linkname unsafe_New reflect.unsafe_New
func unsafe_New(*rtype) uintptr func unsafe_New(*rtype) uintptr
func (d *ptrDecoder) setDisallowUnknownFields(disallowUnknownFields bool) {
d.dec.setDisallowUnknownFields(disallowUnknownFields)
}
func (d *ptrDecoder) decodeStream(s *stream, p uintptr) error { func (d *ptrDecoder) decodeStream(s *stream, p uintptr) error {
newptr := unsafe_New(d.typ) newptr := unsafe_New(d.typ)
if err := d.dec.decodeStream(s, newptr); err != nil { if err := d.dec.decodeStream(s, newptr); err != nil {

View File

@ -56,6 +56,10 @@ func copySlice(elemType *rtype, dst, src reflect.SliceHeader) int
//go:linkname newArray reflect.unsafe_NewArray //go:linkname newArray reflect.unsafe_NewArray
func newArray(*rtype, int) unsafe.Pointer func newArray(*rtype, int) unsafe.Pointer
func (d *sliceDecoder) setDisallowUnknownFields(disallowUnknownFields bool) {
d.valueDecoder.setDisallowUnknownFields(disallowUnknownFields)
}
func (d *sliceDecoder) decodeStream(s *stream, p uintptr) error { func (d *sliceDecoder) decodeStream(s *stream, p uintptr) error {
for { for {
switch s.char() { switch s.char() {

View File

@ -11,6 +11,8 @@ func newStringDecoder() *stringDecoder {
return &stringDecoder{} return &stringDecoder{}
} }
func (d *stringDecoder) setDisallowUnknownFields(_ bool) {}
func (d *stringDecoder) decodeStream(s *stream, p uintptr) error { func (d *stringDecoder) decodeStream(s *stream, p uintptr) error {
bytes, err := d.decodeStreamByte(s) bytes, err := d.decodeStreamByte(s)
if err != nil { if err != nil {

View File

@ -1,6 +1,7 @@
package json package json
import ( import (
"fmt"
"unsafe" "unsafe"
) )
@ -12,6 +13,7 @@ type structFieldSet struct {
type structDecoder struct { type structDecoder struct {
fieldMap map[string]*structFieldSet fieldMap map[string]*structFieldSet
keyDecoder *stringDecoder keyDecoder *stringDecoder
disallowUnknownFields bool
} }
func newStructDecoder(fieldMap map[string]*structFieldSet) *structDecoder { func newStructDecoder(fieldMap map[string]*structFieldSet) *structDecoder {
@ -21,6 +23,13 @@ func newStructDecoder(fieldMap map[string]*structFieldSet) *structDecoder {
} }
} }
func (d *structDecoder) setDisallowUnknownFields(disallowUnknownFields bool) {
d.disallowUnknownFields = disallowUnknownFields
for _, field := range d.fieldMap {
field.dec.setDisallowUnknownFields(disallowUnknownFields)
}
}
func (d *structDecoder) decodeStream(s *stream, p uintptr) error { func (d *structDecoder) decodeStream(s *stream, p uintptr) error {
s.skipWhiteSpace() s.skipWhiteSpace()
if s.char() == nul { if s.char() == nul {
@ -56,6 +65,8 @@ func (d *structDecoder) decodeStream(s *stream, p uintptr) error {
if err := field.dec.decodeStream(s, p+field.offset); err != nil { if err := field.dec.decodeStream(s, p+field.offset); err != nil {
return err return err
} }
} else if d.disallowUnknownFields {
return fmt.Errorf("json: unknown field %q", k)
} else { } else {
if err := s.skipValue(); err != nil { if err := s.skipValue(); err != nil {
return err return err
@ -110,6 +121,8 @@ func (d *structDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, erro
return 0, err return 0, err
} }
cursor = c cursor = c
} else if d.disallowUnknownFields {
return 0, fmt.Errorf("json: unknown field %q", k)
} else { } else {
c, err := skipValue(buf, cursor) c, err := skipValue(buf, cursor)
if err != nil { if err != nil {

View File

@ -225,6 +225,21 @@ func Test_Decoder_UseNumber(t *testing.T) {
assertEq(t, "json.Number", "json.Number", fmt.Sprintf("%T", v["a"])) assertEq(t, "json.Number", "json.Number", fmt.Sprintf("%T", v["a"]))
} }
func Test_Decoder_DisallowUnknownFields(t *testing.T) {
dec := json.NewDecoder(strings.NewReader(`{"x": 1}`))
dec.DisallowUnknownFields()
var v struct {
x int
}
err := dec.Decode(&v)
if err == nil {
t.Fatal("expected unknown field error")
}
if err.Error() != `json: unknown field "x"` {
t.Fatal("expected unknown field error")
}
}
type unmarshalJSON struct { type unmarshalJSON struct {
v int v int
} }

View File

@ -24,6 +24,8 @@ func (d *uintDecoder) parseUint(b []byte) uint64 {
return sum return sum
} }
func (d *uintDecoder) setDisallowUnknownFields(_ bool) {}
func (d *uintDecoder) decodeStreamByte(s *stream) ([]byte, error) { func (d *uintDecoder) decodeStreamByte(s *stream) ([]byte, error) {
for { for {
switch s.char() { switch s.char() {

View File

@ -12,6 +12,8 @@ func newUnmarshalJSONDecoder(typ *rtype) *unmarshalJSONDecoder {
return &unmarshalJSONDecoder{typ: typ} return &unmarshalJSONDecoder{typ: typ}
} }
func (d *unmarshalJSONDecoder) setDisallowUnknownFields(_ bool) {}
func (d *unmarshalJSONDecoder) decodeStream(s *stream, p uintptr) error { func (d *unmarshalJSONDecoder) decodeStream(s *stream, p uintptr) error {
s.skipWhiteSpace() s.skipWhiteSpace()
start := s.cursor start := s.cursor

View File

@ -13,6 +13,8 @@ func newUnmarshalTextDecoder(typ *rtype) *unmarshalTextDecoder {
return &unmarshalTextDecoder{typ: typ} return &unmarshalTextDecoder{typ: typ}
} }
func (d *unmarshalTextDecoder) setDisallowUnknownFields(_ bool) {}
func (d *unmarshalTextDecoder) decodeStream(s *stream, p uintptr) error { func (d *unmarshalTextDecoder) decodeStream(s *stream, p uintptr) error {
s.skipWhiteSpace() s.skipWhiteSpace()
start := s.cursor start := s.cursor