Skip to content
This repository was archived by the owner on Dec 15, 2025. It is now read-only.
This repository was archived by the owner on Dec 15, 2025. It is now read-only.

Recursive struct type causes stack overflow / Handling of overlapping field tagged name is not aligned to std #657

Description

@ngicks
  • 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.
  • 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.

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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions