samber / samber/mo

Option JSON serialization/deserialization does not handle the null case correctly

Open
#72 9 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
3.4k
Forks
120
Avg merge
4d 17h
Merged PRs (30d)
3

Description

Hi,

I believe something is wrong in the behavior of Option's UnmarshalJSON.
Say I have the very basic struct :

type Person struct {
	Name string
	Age  mo.Option[int]
}

and a test that ensures that the person can be serialized to JSON back and forth (roundtrip) :

func TestRoundtripJSONPerson(t *testing.T) {
	person := bugmooption.Person{
		Name: "MyPerson",
		Age:  mo.None[int](),
	}

	serialized, err := json.Marshal(&person)
        if err != nil {
		t.Fatalf("Failed to serialize Person")
	}

	var deserialized bugmooption.Person
	err := json.Unmarshal(serialized, &deserialized)
	if err != nil {
		t.Fatalf("Failed to deserialize Person")
	}

	if person != deserialized {
		t.Fatalf("person should be the same before and after JSON serialization roundtrip")
	}
}

The test currently fails. Indeed, after serializing, my person has a JSON field age: null.
After deserializing, I get an age which is of the form :

{
    value: 0,
    isPresent: true,
}

So I've had a look at the implementation. At a glance, this comment :

// if user manually set the field to be `null`, then `isPresent` should be `true` since that is want user intends the value to be
// this makes sure `isPresent` makes semantic sense, and solves the ambiguity of pointer completely

sounds very wrong to me. If the field is set to null, then I expect isPresent to be false, not true.

I'm not sure why no one else has found this behavior strange, am I missing something ?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at Option's UnmarshalJSON implementation and reproduce the roundtrip shown with a Person whose Age is None. Compare the serialized null with the deserialized Option state; done means the roundtrip preserves the absent value and the relevant JSON behavior is covered by a regression test.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
api
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.