Skip to content
Open
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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ generate: build
bin/easyjson -build_tags=use_easyjson -disable_members_unescape ./benchmark/data.go
bin/easyjson -disallow_unknown_fields ./tests/disallow_unknown.go
bin/easyjson -disable_members_unescape ./tests/members_unescaped.go
bin/easyjson -disallow_duplicate_fields ./tests/duplicate_fields.go

test: generate
go test \
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ Usage of easyjson:
only generate stubs for marshaler/unmarshaler funcs
-disallow_unknown_fields
return error if some unknown field in json appeared
-disallow_duplicate_fields
return error if a field appears in the json more than once
-disable_members_unescape
disable unescaping of \uXXXX string sequences in member names
```
Expand Down
4 changes: 4 additions & 0 deletions bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Generator struct {
OmitEmpty bool
DisallowUnknownFields bool
SkipMemberNameUnescaping bool
DisallowDuplicateFields bool

OutName string
BuildTags string
Expand Down Expand Up @@ -137,6 +138,9 @@ func (g *Generator) writeMain() (path string, err error) {
if g.SkipMemberNameUnescaping {
fmt.Fprintln(f, " g.SkipMemberNameUnescaping()")
}
if g.DisallowDuplicateFields {
fmt.Fprintln(f, " g.DisallowDuplicateFields()")
}

sort.Strings(g.Types)
for _, v := range g.Types {
Expand Down
2 changes: 2 additions & 0 deletions easyjson/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ var noformat = flag.Bool("noformat", false, "do not run 'gofmt -w' on output fil
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")
var disallowDuplicateFields = flag.Bool("disallow_duplicate_fields", false, "return error if a field appears in the json more than once")
var skipMemberNameUnescaping = flag.Bool("disable_members_unescape", false, "don't perform unescaping of member names to improve performance")

func generate(fname string) (err error) {
Expand Down Expand Up @@ -78,6 +79,7 @@ func generate(fname string) (err error) {
LowerCamelCase: *lowerCamelCase,
NoStdMarshalers: *noStdMarshalers,
DisallowUnknownFields: *disallowUnknownFields,
DisallowDuplicateFields: *disallowDuplicateFields,
SkipMemberNameUnescaping: *skipMemberNameUnescaping,
OmitEmpty: *omitEmpty,
LeaveTemps: *leaveTemps,
Expand Down
10 changes: 8 additions & 2 deletions gen/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,11 +343,17 @@ func (g *Generator) genStructFieldDecoder(t reflect.Type, f reflect.StructField)
}

fmt.Fprintf(g.out, " case %q:\n", jsonName)
if g.disallowDuplicateFields {
g.imports["fmt"] = "fmt"
fmt.Fprintf(g.out, " if %sSet {\n", f.Name)
fmt.Fprintf(g.out, " in.AddError(fmt.Errorf(\"duplicate field %s\"))\n", f.Name)
fmt.Fprintf(g.out, " }\n")
}
if err := g.genTypeDecoder(f.Type, "out."+f.Name, tags, 3); err != nil {
return err
}

if tags.required {
if g.disallowDuplicateFields || tags.required {
fmt.Fprintf(g.out, "%sSet = true\n", f.Name)
}

Expand All @@ -357,7 +363,7 @@ func (g *Generator) genStructFieldDecoder(t reflect.Type, f reflect.StructField)
func (g *Generator) genRequiredFieldSet(t reflect.Type, f reflect.StructField) {
tags := parseFieldTags(f)

if !tags.required {
if !g.disallowDuplicateFields && !tags.required {
return
}

Expand Down
6 changes: 6 additions & 0 deletions gen/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type Generator struct {
fieldNamer FieldNamer
simpleBytes bool
skipMemberNameUnescaping bool
disallowDuplicateFields bool

// package path to local alias map for tracking imports
imports map[string]string
Expand Down Expand Up @@ -123,6 +124,11 @@ func (g *Generator) SkipMemberNameUnescaping() {
g.skipMemberNameUnescaping = true
}

// DisallowDuplicateFields instructs to error when a field is defined more than once
func (g *Generator) DisallowDuplicateFields() {
g.disallowDuplicateFields = true
}

// OmitEmpty triggers `json=",omitempty"` behaviour by default.
func (g *Generator) OmitEmpty() {
g.omitEmpty = true
Expand Down
7 changes: 7 additions & 0 deletions tests/duplicate_fields.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package tests

//easyjson:json
type Dupl struct {
A int `json:"a"`
B int `json:"b"`
}
39 changes: 39 additions & 0 deletions tests/duplicate_fields_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package tests

import (
"testing"

"github.com/mailru/easyjson"
)

func TestDuplicateFieldError(t *testing.T) {
var d Dupl
err := easyjson.Unmarshal([]byte(`{"a": 1, "a": 2}`), &d)
if err == nil || err.Error() != "duplicate field A" {
t.Error("want error, got ", err)
}
}

func TestDuplicateFieldNoError(t *testing.T) {
var d Dupl
err := easyjson.Unmarshal([]byte(`{"a": 1, "b": 2}`), &d)
if err != nil {
t.Error("unexpected error ", err)
}
if d != (Dupl{A: 1, B: 2}) {
t.Error("unexpected value ", d)
}
}

func TestDuplicateFieldNoErrorWithoutOption(t *testing.T) {
// We use the DisallowUnknown struct, which is generated without the duplicate field check enabled,
// so we expect to get no error and the last given value.
var d DisallowUnknown
err := easyjson.Unmarshal([]byte(`{"field_one": "a", "field_one": "b"}`), &d)
if err != nil {
t.Error("unexpected error ", err)
}
if d != (DisallowUnknown{FieldOne: "b"}) {
t.Error("unexpected value ", d)
}
}