Proposal Details
In its current implementation, json/v2 (and v1, for that matter) does not check whether JSON object names, as per a struct's fields or their respective JSON name tags, do actually exist in the data that is being unmarshaled. For example:
Let our JSON string be:
{
"a": 123,
"b": "hello",
}
And our struct:
type Foo struct {
A int `json:"a"`
B *string `json:"b"`
C int `json:"c"`
}
Unmarshaling the JSON string into an instance of Foo will result in C being 0. To distinguish "c": 0 from a nonexistent key "c", we'd need to unmarshal again, but this time into an instance of map[string]any and then check for the existence of c. This would be both cumbersome and inefficient because we'd have to unmarshal twice. Consequently, unmarshaling should fail if a given key is not found.
Now let's assume that the existence of each key is mandatory. What if we wanted C to be optional? This could be achieved by introducing two new JSON tags: optional and nullable: This could be achieved by introducing an optional tag:
type Foo struct {
A int `json:"a"`
B *string `json:"b"`
C *int `json:"c,optional"`
}
Note that the type of C is now *int instead of int. A value of nil indicates the absence of "c" in the JSON string. As for B, however, a value of nil indicates a corresponding JSON value of null.
So struct fields with single-pointer types must be either optional or nullable. Setting neither tag is ambiguous and should thus cause an error.
To make C both optional and nullable, we'd simply use a double pointer:
type Foo struct {
A int `json:"a"`
B *string `json:"b"`
C **int `json:"c,optional"`
}
Since a double-pointer type already indicates that a field has to be optional and nullable, using a tag of just json:"c" should also suffice in the above case.
Unfortunately, the mandatory existence of fields breaks backwards compatibility. Thus, it should be disabled by default and enabled by setting a option, e.g. KnownFieldsMustExist.
Proposal Details
In its current implementation,
json/v2(and v1, for that matter) does not check whether JSON object names, as per astruct's fields or their respective JSON name tags, do actually exist in the data that is being unmarshaled. For example:Let our JSON string be:
{ "a": 123, "b": "hello", }And our
struct:Unmarshaling the JSON string into an instance of
Foowill result inCbeing0. To distinguish"c": 0from a nonexistent key"c", we'd need to unmarshal again, but this time into an instance ofmap[string]anyand then check for the existence ofc. This would be both cumbersome and inefficient because we'd have to unmarshal twice. Consequently, unmarshaling should fail if a given key is not found.Now let's assume that the existence of each key is mandatory. What if we wanted
Cto be optional?This could be achieved by introducing two new JSON tags:This could be achieved by introducing anoptionalandnullable:optionaltag:Note that the type of
Cis now*intinstead ofint. A value ofnilindicates the absence of"c"in the JSON string. As forB, however, a value ofnilindicates a corresponding JSON value ofnull.So struct fields with single-pointer types must be either optional or nullable. Setting neither tag is ambiguous and should thus cause an error.To make
Cboth optional and nullable, we'd simply use a double pointer:Since a double-pointer type already indicates that a field has to be optional and nullable, using a tag of just
json:"c"should also suffice in the above case.Unfortunately, the mandatory existence of fields breaks backwards compatibility. Thus, it should be disabled by default and enabled by setting a option, e.g.
KnownFieldsMustExist.