Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error for duplicate fields #575

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Config struct {
SortMapKeys bool
UseNumber bool
DisallowUnknownFields bool
DisallowDuplicateFields bool
TagKey string
OnlyTaggedField bool
ValidateJsonRawMessage bool
Expand Down Expand Up @@ -72,6 +73,7 @@ type frozenConfig struct {
objectFieldMustBeSimpleString bool
onlyTaggedField bool
disallowUnknownFields bool
disallowDuplicateFields bool
decoderCache *concurrent.Map
encoderCache *concurrent.Map
encoderExtension Extension
Expand Down Expand Up @@ -133,6 +135,7 @@ func (cfg Config) Froze() API {
objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
onlyTaggedField: cfg.OnlyTaggedField,
disallowUnknownFields: cfg.DisallowUnknownFields,
disallowDuplicateFields: cfg.DisallowDuplicateFields,
caseSensitive: cfg.CaseSensitive,
}
api.streamPool = &sync.Pool{
Expand Down
129 changes: 129 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"fmt"
"os"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func ExampleMarshal() {
Expand Down Expand Up @@ -119,3 +122,129 @@ func (m *MyKey) UnmarshalText(text []byte) error {
*m = MyKey(text[:3])
return nil
}

type Target struct {
FieldA string `json:"fieldA"`
}

func Example_duplicateFieldsCaseSensitive() {
api := Config{
CaseSensitive: true,
DisallowDuplicateFields: true,
}.Froze()

t := &Target{}
err := api.Unmarshal([]byte(`{"fieldA": "value", "fielda": "val2"}`), t)
fmt.Printf("Case-sensitiveness means no duplicates: 'fieldA' = %q, err = %v\n", t.FieldA, err)

t = &Target{}
err = api.Unmarshal([]byte(`{"fieldA": "value", "fieldA": "val2"}`), t)
fmt.Printf("Got duplicates in struct field: 'fieldA' = %q, err = %v\n", t.FieldA, err)

t = &Target{}
err = api.Unmarshal([]byte(`{"fielda": "value", "fielda": "val2"}`), t)
fmt.Printf("Got duplicates not in struct field: 'fieldA' = %q, err = %v\n", t.FieldA, err)

// Output:
// Case-sensitiveness means no duplicates: 'fieldA' = "value", err = <nil>
// Got duplicates in struct field: 'fieldA' = "value", err = jsoniter.Target.ReadObject: found duplicate field: fieldA, error found in #10 byte of ...|, "fieldA": "val2"}|..., bigger context ...|{"fieldA": "value", "fieldA": "val2"}|...
// Got duplicates not in struct field: 'fieldA' = "", err = jsoniter.Target.ReadObject: found duplicate field: fielda, error found in #10 byte of ...|, "fielda": "val2"}|..., bigger context ...|{"fielda": "value", "fielda": "val2"}|...
}

func Example_noDuplicateFieldsCaseSensitive() {
api := Config{
CaseSensitive: true,
DisallowDuplicateFields: false,
}.Froze()

t := &Target{}
err := api.Unmarshal([]byte(`{"fieldA": "value", "fielda": "val2"}`), t)
fmt.Printf("Case-sensitiveness means no duplicates: 'fieldA' = %q, err = %v\n", t.FieldA, err)

t = &Target{}
err = api.Unmarshal([]byte(`{"fieldA": "value", "fieldA": "val2"}`), t)
fmt.Printf("Got duplicates in struct field: 'fieldA' = %q, err = %v\n", t.FieldA, err)

t = &Target{}
err = api.Unmarshal([]byte(`{"fielda": "value", "fielda": "val2"}`), t)
fmt.Printf("Got duplicates not in struct field: 'fieldA' = %q, err = %v\n", t.FieldA, err)

// Output:
// Case-sensitiveness means no duplicates: 'fieldA' = "value", err = <nil>
// Got duplicates in struct field: 'fieldA' = "val2", err = <nil>
// Got duplicates not in struct field: 'fieldA' = "", err = <nil>
}

func Example_duplicateFieldsInCaseSensitive() {
api := Config{
CaseSensitive: false,
DisallowDuplicateFields: true,
}.Froze()

t := &Target{}
err := api.Unmarshal([]byte(`{"fieldA": "value", "fielda": "val2"}`), t)
fmt.Printf("In-case-sensitive duplicates: 'fieldA' = %q, err = %v\n", t.FieldA, err)

t = &Target{}
err = api.Unmarshal([]byte(`{"fieldA": "value", "fieldA": "val2"}`), t)
fmt.Printf("Got duplicates in exact struct field match: 'fieldA' = %q, err = %v\n", t.FieldA, err)

t = &Target{}
err = api.Unmarshal([]byte(`{"fielda": "value", "fielda": "val2"}`), t)
fmt.Printf("Got duplicates not in notexact struct field match: 'fieldA' = %q, err = %v\n", t.FieldA, err)

// Output:
// In-case-sensitive duplicates: 'fieldA' = "value", err = jsoniter.Target.ReadObject: found duplicate field: fielda, error found in #10 byte of ...|, "fielda": "val2"}|..., bigger context ...|{"fieldA": "value", "fielda": "val2"}|...
// Got duplicates in exact struct field match: 'fieldA' = "value", err = jsoniter.Target.ReadObject: found duplicate field: fieldA, error found in #10 byte of ...|, "fieldA": "val2"}|..., bigger context ...|{"fieldA": "value", "fieldA": "val2"}|...
// Got duplicates not in notexact struct field match: 'fieldA' = "value", err = jsoniter.Target.ReadObject: found duplicate field: fielda, error found in #10 byte of ...|, "fielda": "val2"}|..., bigger context ...|{"fielda": "value", "fielda": "val2"}|...
}

func Example_noDuplicateFieldsInCaseSensitive() {
api := Config{
CaseSensitive: false,
DisallowDuplicateFields: false,
}.Froze()

t := &Target{}
err := api.Unmarshal([]byte(`{"fieldA": "value", "fielda": "val2"}`), t)
fmt.Printf("Case-sensitiveness means no duplicates: 'fieldA' = %q, err = %v\n", t.FieldA, err)

t = &Target{}
err = api.Unmarshal([]byte(`{"fieldA": "value", "fieldA": "val2"}`), t)
fmt.Printf("Got duplicates in struct field: 'fieldA' = %q, err = %v\n", t.FieldA, err)

t = &Target{}
err = api.Unmarshal([]byte(`{"fielda": "value", "fielda": "val2"}`), t)
fmt.Printf("Got duplicates not in struct field: 'fieldA' = %q, err = %v\n", t.FieldA, err)

// Output:
// Case-sensitiveness means no duplicates: 'fieldA' = "val2", err = <nil>
// Got duplicates in struct field: 'fieldA' = "val2", err = <nil>
// Got duplicates not in struct field: 'fieldA' = "val2", err = <nil>
}

func TestEncoder(t *testing.T) {
api := Config{
CaseSensitive: true,
DisallowDuplicateFields: true,
}.Froze()

type target2 struct {
B Target `json:"b"`
A Target `json:"a"`
}

data := `{"a": {"fieldA": "bla"}, "b": {"fieldA": "bar"}}`
data += data

d := api.NewDecoder(strings.NewReader(data))
obj := &target2{}
assert.Nil(t, d.Decode(obj))
assert.Equal(t, "bla", obj.A.FieldA)
assert.Equal(t, "bar", obj.B.FieldA)

obj = &target2{}
assert.Nil(t, d.Decode(obj))
assert.Equal(t, "bla", obj.A.FieldA)
assert.Equal(t, "bar", obj.B.FieldA)
}
6 changes: 1 addition & 5 deletions iter_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package jsoniter

import (
"fmt"
"strings"
)

// ReadObject read one field from object.
Expand Down Expand Up @@ -95,10 +94,7 @@ func (iter *Iterator) readFieldHash() int64 {
}
}

func calcHash(str string, caseSensitive bool) int64 {
if !caseSensitive {
str = strings.ToLower(str)
}
func calcHash(str string) int64 {
hash := int64(0x811c9dc5)
for _, b := range []byte(str) {
hash ^= int64(b)
Expand Down
Loading