This started out as just a comment on a previous answer but grew.
Although you can unmarshal into a temporary map[string]interface{}
(or map[string]json.RawMessage
) that can do a lot of extra work if there are other "outer" fields that you just want to ignore and it also requires checking the type that was found
(e.g. for the case of bad/unexpected input like {"Outer": 42}
).
That probably means adding something like this to the previous answer:
str, ok := m["Outer"].(string)
if !ok { return errors.New("…") }
err := json.Unmarshal([]byte(str), &actual)
Much simpler is probably to use a temporary wrapping type like so:
var tmp struct{ Outer string }
err := json.Unmarshal(b, &tmp)
if err != nil { … }
var data actualType
err := json.Unmarshal([]byte(tmp.Outer), &data)
You could either do this in a separate function that returns the type you wanted (or an error, e.g. func foo(b []byte) (actualType, error)
).
Or you could put a variation of this into a custom UnmarshalJSON
method on a wrapper type so as to implement json.Unmarshaler
.
You could also do the reverse if you wanted to be able to marshal back into the same format.
Full runnable examples on the playground: https://play.golang.org/p/IXU2koyJ1A