With Dustin's approach you could use a temporary map:
func (t *MyType) MarshalJSON() ([]byte, error) {
tmp := make(map[string]interface{})
for k, v := range t.Extra {
tmp[k] = v
}
// overwrite fields in Main into tmp
data, err := json.Marshal(t.Main)
if err != nil {
return nil, err
}
json.Unmarshal(data, &tmp)
return json.Marshal(&tmp)
}
type MyType struct {
Main
Extra json.RawMessage
}
func (t *MyType) MarshalJSON() ([]byte, error) {
return t.Extra, nil
}
func (t *MyType) UnmarshalJSON(p []byte) error {
json.Unmarshal(p, &t.Main)
t.Extra = json.RawMessage(p)
return nil