Description
- Using jsoniter.Marshal against struct types which contain recursive embedded type causes stack overflow.
- std's
encoding/json
prevents this by these lines - Jsoniter seemingly does not have that
visited
map by quick read through of source code.
- std's
- Also handling of overlapping tagged field names are not aligned to what is of std lib.
- std's
encoding/json
describes this behavior in its source code but is not documented.
- std's
Searching through issues with "recursive" does not give me any similar issues.
Are these behaviors intentionally dropped?
> go version
go version go1.20 linux/amd64
require github.com/json-iterator/go v1.1.12
I've uploaded test code here.
https://github.com/ngicks/test-jsoniter
package testjsoniter_test
import (
"encoding/json"
"testing"
jsoniter "github.com/json-iterator/go"
)
type OverlappingKey1 struct {
Foo string
Bar string `json:"Baz"`
Baz string
}
type OverlappingKey2 struct {
Foo string
Bar string `json:"Bar"`
Baz string `json:"Bar"`
}
type OverlappingKey3 struct {
Foo string
Bar string `json:"Baz"`
Baz string
Qux string `json:"Baz"`
}
type Sub1 struct {
Foo string
Bar string `json:"Bar"`
}
type OverlappingKey4 struct {
Foo string
Bar string
Baz string
Sub1
}
type Recursive1 struct {
R string `json:"r"`
Recursive2
}
type Recursive2 struct {
R string `json:"r"`
RR string `json:"rr"`
*OverlappingKey5
}
type OverlappingKey5 struct {
Foo string
Recursive1
}
func TestJsonIter_behavioral_deference(t *testing.T) {
for _, config := range []jsoniter.API{
jsoniter.ConfigCompatibleWithStandardLibrary,
jsoniter.ConfigDefault,
} {
for _, v := range []any{
OverlappingKey1{"foo", "bar", "baz"},
OverlappingKey2{"foo", "bar", "baz"},
OverlappingKey3{"foo", "bar", "baz", "qux"},
OverlappingKey4{Foo: "foo", Bar: "bar", Baz: "baz", Sub1: Sub1{Foo: "foofoo", Bar: "barbar"}},
// These cause stack overflow
// OverlappingKey5{Foo: "foo", Recursive1: Recursive1{R: "r", Recursive2: Recursive2{R: "rr"}}},
// OverlappingKey5{Foo: "foo", Recursive1: Recursive1{R: "r", Recursive2: Recursive2{R: "rr", OverlappingKey5: &OverlappingKey5{Foo: "foo"}}}},
} {
binOrg, err := json.Marshal(v)
if err != nil {
panic(err)
}
binMimic, err := config.Marshal(v)
if err != nil {
panic(err)
}
str1, str2 := string(binOrg), string(binMimic)
if str1 != str2 {
t.Errorf("not same, type = %T. org = %s, jsoniter = %s\n", v, str1, str2)
}
}
}
}
This gives result of
> go test -v ./...
=== RUN TestJsonIter_behavioral_deference
recursive_test.go:82: not same, type = testjsoniter_test.OverlappingKey1. org = {"Foo":"foo","Baz":"bar"}, jsoniter = {"Foo":"foo","Baz":"baz"}
recursive_test.go:82: not same, type = testjsoniter_test.OverlappingKey3. org = {"Foo":"foo"}, jsoniter = {"Foo":"foo","Baz":"qux"}
recursive_test.go:82: not same, type = testjsoniter_test.OverlappingKey4. org = {"Foo":"foo","Bar":"bar","Baz":"baz"}, jsoniter = {"Foo":"foo","Baz":"baz","Bar":"barbar"}
recursive_test.go:82: not same, type = testjsoniter_test.OverlappingKey1. org = {"Foo":"foo","Baz":"bar"}, jsoniter = {"Foo":"foo","Baz":"baz"}
recursive_test.go:82: not same, type = testjsoniter_test.OverlappingKey3. org = {"Foo":"foo"}, jsoniter = {"Foo":"foo","Baz":"qux"}
recursive_test.go:82: not same, type = testjsoniter_test.OverlappingKey4. org = {"Foo":"foo","Bar":"bar","Baz":"baz"}, jsoniter = {"Foo":"foo","Baz":"baz","Bar":"barbar"}
--- FAIL: TestJsonIter_behavioral_deference (0.00s)
FAIL
FAIL github.com/ngicks/test-jsoniter.git 0.002s
FAIL
Metadata
Metadata
Assignees
Labels
No labels