encoding/json.Unmarshal
merges structs but not map values
Let’s say we have a struct type with two fields
type foo struct {
A int `json:"a"`
B int `json:"b"`
}
and we try to unmarshal two JSON payloads {"a": 1}
and {"b": 1}
on it.
The resulting value would be {A: 1, B: 1}
:
i.e. we can say that json.Unmarshal
would merge the existing value with the one coming from the JSON payload.
However, if our type is map[string]foo
and we try to unmarshal two JSON payloads:
{
"key": {"a": 1}
}
and
{
"key": {"b": 1}
}
Then the result would be just map[key:{A:0 B:1}]
:
i.e., the second JSON payload would completely overwrite the first one, instead of merging them.
More examples
Struct with struct field
When the outer type is defined as a specific struct instead of a map:
type foo struct {
A int `json:"a"`
B int `json:"b"`
}
type bar struct {
Key foo `json:"key"`
}
Then the result is properly merged, producing the expected {Key:{A:1 B:1}}
as the result.
Different map keys
On the other hand, when different map keys are unmarshaled, like {"key1": {"a": 1}}
and {"key2": {"b": 1}}
,
then the outer map is merged too, resulting in map[key1:{A:1 B:0} key2:{A:0 B:1}]
.
Merging slice values
Finally, when two JSON arrays of one element, [{"a": 1}]
and [{"b": 1}]
are unmarshaled on the same slice []foo
,
then the resulting first element of the slice is merged again, producing [{A:1 B:1}]
.
Why?
The issue golang/go#33487 has a discussion around it,
and the main argument is that json
package does not perform recursive merging of values, like map values.
This doesn’t seem to apply to slice values, however, which are merged as was shown before.
YAML and friends
This issue can also be reproduced with other format unmarshaling libraries, like gopkg.in/yaml.v3
,
and it obviously depends on the implementation of each one, but they seem to be largerly consistent on this inconsistency.