diff --git a/.gitignore b/.gitignore index db8c66e..26156fb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *_easyjson.go *.iml .idea +*.swp diff --git a/Makefile b/Makefile index 49c80f3..7717c1e 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,7 @@ generate: root build .root/bin/easyjson .root/src/$(PKG)/tests/named_type.go .root/bin/easyjson .root/src/$(PKG)/tests/custom_map_key_type.go .root/bin/easyjson .root/src/$(PKG)/tests/embedded_type.go + .root/bin/easyjson -disallow_unknown_fields .root/src/$(PKG)/tests/disallow_unknown.go test: generate root go test \ diff --git a/README.md b/README.md index 9366e3f..7fd7686 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,8 @@ Usage of easyjson: use lowerCamelCase instead of CamelCase by default -stubs only generate stubs for marshaler/unmarshaler funcs + -disallow_unknown_fields + return error if some unknown field in json appeared ``` Using `-all` will generate marshalers/unmarshalers for all Go structs in the diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index 3c20e09..95e5d1e 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -22,10 +22,11 @@ type Generator struct { PkgPath, PkgName string Types []string - NoStdMarshalers bool - SnakeCase bool - LowerCamelCase bool - OmitEmpty bool + NoStdMarshalers bool + SnakeCase bool + LowerCamelCase bool + OmitEmpty bool + DisallowUnknownFields bool OutName string BuildTags string @@ -120,6 +121,9 @@ func (g *Generator) writeMain() (path string, err error) { if g.NoStdMarshalers { fmt.Fprintln(f, " g.NoStdMarshalers()") } + if g.DisallowUnknownFields { + fmt.Fprintln(f, " g.DisallowUnknownFields()") + } sort.Strings(g.Types) for _, v := range g.Types { diff --git a/easyjson/main.go b/easyjson/main.go index 1cd30bb..d4035f7 100644 --- a/easyjson/main.go +++ b/easyjson/main.go @@ -27,6 +27,7 @@ var stubs = flag.Bool("stubs", false, "only generate stubs for marshaler/unmarsh var noformat = flag.Bool("noformat", false, "do not run 'gofmt -w' on output file") var specifiedName = flag.String("output_filename", "", "specify the filename of the output") var processPkg = flag.Bool("pkg", false, "process the whole package instead of just the given file") +var disallowUnknownFields = flag.Bool("disallow_unknown_fields", false, "return error if any unknown field in json appeared") func generate(fname string) (err error) { fInfo, err := os.Stat(fname) @@ -60,18 +61,19 @@ func generate(fname string) (err error) { } g := bootstrap.Generator{ - BuildTags: trimmedBuildTags, - PkgPath: p.PkgPath, - PkgName: p.PkgName, - Types: p.StructNames, - SnakeCase: *snakeCase, - LowerCamelCase: *lowerCamelCase, - NoStdMarshalers: *noStdMarshalers, - OmitEmpty: *omitEmpty, - LeaveTemps: *leaveTemps, - OutName: outName, - StubsOnly: *stubs, - NoFormat: *noformat, + BuildTags: trimmedBuildTags, + PkgPath: p.PkgPath, + PkgName: p.PkgName, + Types: p.StructNames, + SnakeCase: *snakeCase, + LowerCamelCase: *lowerCamelCase, + NoStdMarshalers: *noStdMarshalers, + DisallowUnknownFields: *disallowUnknownFields, + OmitEmpty: *omitEmpty, + LeaveTemps: *leaveTemps, + OutName: outName, + StubsOnly: *stubs, + NoFormat: *noformat, } if err := g.Run(); err != nil { diff --git a/gen/decoder.go b/gen/decoder.go index 3c8f8f8..5dc1249 100644 --- a/gen/decoder.go +++ b/gen/decoder.go @@ -461,7 +461,15 @@ func (g *Generator) genStructDecoder(t reflect.Type) error { } fmt.Fprintln(g.out, " default:") - fmt.Fprintln(g.out, " in.SkipRecursive()") + if g.disallowUnknownFields { + fmt.Fprintln(g.out, ` in.AddError(&jlexer.LexerError{ + Offset: in.GetPos(), + Reason: "unknown field", + Data: key, + })`) + } else { + fmt.Fprintln(g.out, " in.SkipRecursive()") + } fmt.Fprintln(g.out, " }") fmt.Fprintln(g.out, " in.WantComma()") fmt.Fprintln(g.out, " }") diff --git a/gen/generator.go b/gen/generator.go index 4f1eb04..a34a852 100644 --- a/gen/generator.go +++ b/gen/generator.go @@ -33,9 +33,10 @@ type Generator struct { varCounter int - noStdMarshalers bool - omitEmpty bool - fieldNamer FieldNamer + noStdMarshalers bool + omitEmpty bool + disallowUnknownFields bool + fieldNamer FieldNamer // package path to local alias map for tracking imports imports map[string]string @@ -110,6 +111,11 @@ func (g *Generator) NoStdMarshalers() { g.noStdMarshalers = true } +// DisallowUnknownFields instructs not to skip unknown fields in json and return error. +func (g *Generator) DisallowUnknownFields() { + g.disallowUnknownFields = true +} + // OmitEmpty triggers `json=",omitempty"` behaviour by default. func (g *Generator) OmitEmpty() { g.omitEmpty = true diff --git a/tests/basic_test.go b/tests/basic_test.go index 28f0fdf..ab166f3 100644 --- a/tests/basic_test.go +++ b/tests/basic_test.go @@ -232,3 +232,11 @@ func TestUnmarshalStructWithEmbeddedPtrStruct(t *testing.T) { t.Errorf("easyjson.Unmarshal() = %#v; want %#v", s, structWithInterfaceValueFilled) } } + +func TestDisallowUnknown(t *testing.T) { + var d DisallowUnknown + err := easyjson.Unmarshal([]byte(disallowUnknownString), &d) + if err == nil { + t.Error("want error, got nil") + } +} diff --git a/tests/disallow_unknown.go b/tests/disallow_unknown.go new file mode 100644 index 0000000..5b884c6 --- /dev/null +++ b/tests/disallow_unknown.go @@ -0,0 +1,8 @@ +package tests + +//easyjson:json +type DisallowUnknown struct { + FieldOne string `json:"field_one"` +} + +var disallowUnknownString = `{"field_one": "one", "field_two": "two"}`