---
title: `encoding/json.Unmarshal` merges structs but not map values
description: Golang package JSON does not merge map values when decoding.
---
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.
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.
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}]
.
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}]
.
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.
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.