Skip to content

Conversation

12Clock
Copy link

@12Clock 12Clock commented Jul 3, 2025

fix: The typ of emptyInterface does not match ptr

Issue:
When struct A contains a single field that is a pointer to struct B (e.g., A { B *B }),encoding an instance of A{} into our custom emptyInterface causes a value-pointer mismatch:

  • emptyInterface.typ points to the type descriptor of A
  • emptyInterface.ptr points directly to the embedded B field (type *B)

Solution:
Encode &A{} (pointer to struct) instead of A{} (struct value). This ensures:

  1. EmptyInterface.typ correctly points to *A
  2. EmptyInterface.ptr holds the address of the A instance, which contains a valid *B field
  3. Type assertions and reflection operations on the interface now work as expected

Fixes #519, #510

@12Clock
Copy link
Author

12Clock commented Jul 4, 2025

Benchmark

Before fixing:

goos: darwin
goarch: arm64
pkg: benchmark
cpu: Apple M3 Pro
Benchmark_Encode_LargeStruct_GoJson-12    	  101646	     10220 ns/op	   14143 B/op	     319 allocs/op
Benchmark_Encode_MediumStruct_GoJson-12    	 2392053	       486.4 ns/op	     608 B/op	      15 allocs/op
Benchmark_Encode_SmallStruct_GoJson-12    	 8177296	       130.6 ns/op	     256 B/op	       2 allocs/op
PASS

After fixing:

goos: darwin
goarch: arm64
pkg: benchmark
cpu: Apple M3 Pro
Benchmark_Encode_LargeStruct_GoJson-12    	  107654	     10314 ns/op	   14147 B/op	     319 allocs/op
Benchmark_Encode_MediumStruct_GoJson-12    	 2375899	       495.5 ns/op	     608 B/op	      15 allocs/op
Benchmark_Encode_SmallStruct_GoJson-12    	 8273752	       138.5 ns/op	     256 B/op	       2 allocs/op
PASS

Testing

Passed all encode tests except Issue147

TestIssue147
    encode_test.go:1795: json: error calling MarshalJSON for type *json_test.testNullStr: unexpected character 'b'

Although Issue147 was wrong when testing, I think it was as expected. 'json_test.testNullStr' should return "b" instead of b

type testNullStr string

func (v *testNullStr) MarshalJSON() ([]byte, error) {
	if *v == "" {
		return []byte("null"), nil
	}

	return []byte(*v), nil
}

func TestIssue147(t *testing.T) {
	type T struct {
		Field1 string      `json:"field1"`
		Field2 testNullStr `json:"field2,omitempty"`
	}
	got, err := json.Marshal(T{
		Field1: "a",
		Field2: "b",
	})
	if err != nil {
		t.Fatal(err)
	}
	expect, _ := stdjson.Marshal(T{
		Field1: "a",
		Field2: "b",
	})
	if !bytes.Equal(expect, got) {
		t.Fatalf("expect %q but got %q", string(expect), string(got))
	}
}

@12Clock
Copy link
Author

12Clock commented Jul 11, 2025

@goccy Could you take a look at this PR when you have a moment?

encode.go Outdated
rv := reflect.ValueOf(v)
// struct to *struct,fix: https://github.com/goccy/go-json/issues/519
if rv.Kind() == reflect.Struct {
newV := reflect.New(rv.Type())
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this approach, allocation occurs every time in the case of a struct, which is not ideal in terms of performance.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@goccy Is it feasible to change 'rv.Kind() == reflect.Struct' to 'rv.Kind() == reflect.Struct && rv.NumField() == 1'?

@codecov-commenter
Copy link

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 91.66667% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 74.71%. Comparing base (9872089) to head (93c6385).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #551      +/-   ##
==========================================
- Coverage   76.73%   74.71%   -2.03%     
==========================================
  Files          55       55              
  Lines       18926    18935       +9     
==========================================
- Hits        14523    14147     -376     
- Misses       3771     4108     +337     
- Partials      632      680      +48     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Panic in Go Application When Marshaling Structs
4 participants