Skip to content

Preserve null values when converting nil containers to JSON - #171

Open
x0Lazarus wants to merge 2 commits into
stretchr:masterfrom
x0Lazarus:fix/preserve-json-null-containers
Open

x0Lazarus wants to merge 2 commits into
stretchr:masterfrom
x0Lazarus:fix/preserve-json-null-containers

Conversation

@x0Lazarus

@x0Lazarus x0Lazarus commented Sep 26, 2026 •

Copy link
Copy Markdown

Summary

JSON conversion currently changes some nil slices and maps into empty arrays and objects. For example, objx.Map{"items": []interface{}(nil)} becomes {"items":[]} instead of {"items":null}. This loses the distinction between a null collection and an empty one; nil plain string-keyed maps and nil string slices already retain it.

Return nil from the existing cleanup helpers before allocating their output. Nil containers now remain null, while non-nil empty containers still become [] or {}. No new map types or recursive conversion paths are added.

The regression table covers nil, empty and populated containers directly and inside another map or slice. It checks JSON, MustJSON and the decoded Base64 output. All 54 cases pass; 15 fail before this change. The table cases run in parallel, with a per-iteration copy for older Go versions. The full suite and vet pass on Windows with Go 1.23.0 and 1.27.1. Five shuffled full-suite runs with the race detector also pass on Go 1.27.1.

Checklist

  • Tests are passing: the go test -race ./... command used by task test passes locally with CGO enabled. Local validation covers Windows; the updated Ubuntu CI matrix is pending.
  • Code style is correct: gofmt -d -s on the committed LF content of all Go files produces no changes, and go vet ./... passes.

@hanzei hanzei left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks

Comment thread conversions_nil_test.go
{"populated interface map", map[interface{}]interface{}{1: "value"}, `{"1":"value"}`},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we t.Parralell() these tests?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Added t.Parallel() to the table cases and captured the loop variable for Go 1.20/1.21. The full suite and five shuffled race-detector runs pass on Windows.

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.

2 participants