handling - Custom MarshalJSON() nunca recibe una llamada en Go
json stringify golang (1)
He escrito versiones personalizadas de MarshalJSON
y UnmarshalJSON
. Mi UnmarshalJSON
se llama de la manera que quiero, pero no puedo hacerlo funcionar con MarshalJSON
. Aquí está el código que resume mi problema:
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"
}
En resumen, el programa codifica la struct
"automáticamente" de dos maneras y, finalmente, llama a MarshalJSON
manualmente. La respuesta que quiero es "charlie"
. Ejecutar el código genera el siguiente resultado:
{"data":"bravo"}
{"data":"bravo"}
{"data":"charlie"}
Pruébelo en Go Playground: http://play.golang.org/p/SJ05S8rAYN
En esta parte del código, ms
se copia en una variable de interface{}
:
// Trying another method (UNSUCCESSFUL)
if ret, err := json.Marshal(ms); err != nil {
El problema es que esta variable no implementa la interfaz json.Marshaler
, ya que MarshalJSON
no está en el conjunto de métodos para myStruct
(solo para *myStruct
).
La solución es (a) hacer que su método MarshalJSON
tome un receptor sin puntero (lo que significa que obtiene una copia de la estructura: posiblemente sea costoso si es grande), o (b) marque un puntero a la estructura (como Kavu mencionado en un comentario).
El motivo de este comportamiento es que Go no le permite llevar un puntero al valor almacenado dentro de una variable de interfaz, en lugar de eso, le exige que haga una copia del valor cada vez que desee acceder a él. Si bien el lenguaje tiene azúcar sintáctico para convertir ms.MarshalJSON()
en (&ms).MarshalJSON()
como una forma de acceder al método con un receptor de puntero, esto no puede hacerse para un valor almacenado en una variable de interfaz. Por esta razón, no se considera que el método esté en su conjunto de métodos.