Custom MarshalJSON() never gets called in Go
In this part of the code, ms
gets copied into an interface{}
variable:
// Trying another method (UNSUCCESSFUL)
if ret, err := json.Marshal(ms); err != nil {
The problem is that this variable does not implement the json.Marshaler
interface, since MarshalJSON
is not in the method set for myStruct
(only for *myStruct
).
The fix is to either (a) make your MarshalJSON
method take a non-pointer receiver (which will mean it gets a copy of the struct: possibly costly if it is large), or (b) marshal a pointer to the struct (as Kavu mentioned in a comment).
The reason for this behaviour is that Go doesn't let you take a pointer to the value stored inside an interface variable, instead requiring you to make a copy of the value whenever you want to access it. While the language has syntactic sugar to convert ms.MarshalJSON()
into (&ms).MarshalJSON()
as a way to access the method with a pointer receiver, this can not be done for a value stored in an interface variable. For this reason, the method is not considered to be in its method set.
Anders Sjöqvist
Educations in Business and Computer Science. Currently working with development at a startup in Beijing, China.
Updated on June 04, 2022Comments
-
Anders Sjöqvist almost 2 years
I've written custom versions of
MarshalJSON
andUnmarshalJSON
. MyUnmarshalJSON
gets called the way I want it to, but I can't get it to work withMarshalJSON
. Here's code that summarizes my problem:package main import ( "bytes" "encoding/json" "fmt" "log" "os" ) type myStruct struct { Data string `json:"data"` } func (s *myStruct) MarshalJSON() ([]byte, error) { return []byte(`{"data":"charlie"}`), nil } func (s *myStruct) UnmarshalJSON(b []byte) error { // Insert the string directly into the Data member return json.Unmarshal(b, &s.Data) } func main() { // Create a struct with initial content "alpha" ms := myStruct{"alpha"} // Replace content with "bravo" using custom UnmarshalJSON() (SUCCESSFUL) if err := json.NewDecoder(bytes.NewBufferString(`"bravo"`)).Decode(&ms); err != nil { log.Fatal(err) } // Use custom MarshalJSON() to get "charlie" back (UNSUCCESSFUL) if err := json.NewEncoder(os.Stdout).Encode(ms); err != nil { log.Fatal(err) } // Trying another method (UNSUCCESSFUL) if ret, err := json.Marshal(ms); err != nil { log.Fatal(err) } else { fmt.Println(string(ret)) } // Verify that the Marshaler interface is correctly implemented var marsh json.Marshaler marsh = &ms ret, _ := marsh.MarshalJSON() fmt.Println(string(ret)) // Prints "charlie" }
In short, the program encodes the
struct
"automatically" in two ways, and then finally callsMarshalJSON
manually. The response I want is"charlie"
. Running the code generates the following output:{"data":"bravo"} {"data":"bravo"} {"data":"charlie"}
Try it at Go Playground: http://play.golang.org/p/SJ05S8rAYN
-
Valerio almost 10 yearsvery instructive,
&myStruct{}
is a life-saving pattern