Пользовательский маршал JSON() никогда не будет вызван в Go
Я написал собственные версии MarshalJSON
и UnmarshalJSON
. Мой UnmarshalJSON
называется так, как я хочу, но я не могу заставить его работать с MarshalJSON
. Здесь код, который суммирует мою проблему:
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"
}
Короче говоря, программа автоматически кодирует struct
"автоматически" двумя способами, а затем, наконец, вызывает MarshalJSON
вручную. Ответ, который я хочу, - "charlie"
. Запуск кода генерирует следующий вывод:
{"data":"bravo"}
{"data":"bravo"}
{"data":"charlie"}
Попробуйте на Go Playground: http://play.golang.org/p/SJ05S8rAYN
Ответы
Ответ 1
В этой части кода ms
копируется в переменную interface{}
:
// Trying another method (UNSUCCESSFUL)
if ret, err := json.Marshal(ms); err != nil {
Проблема заключается в том, что эта переменная не реализует интерфейс json.Marshaler
, поскольку MarshalJSON
не находится в метод set для myStruct
(только для *myStruct
).
Исправление состоит в том, чтобы либо (a) сделать ваш метод MarshalJSON
принимать приемник без указателя (что будет означать, что он получит копию структуры: возможно, дорогостоящий, если он большой), или (b) маршал указателя к структуре (как упоминал Каву в комментарии).
Причиной такого поведения является то, что Go не позволяет вам указывать указатель на значение, хранящееся внутри переменной интерфейса, вместо этого требуется, чтобы вы делали копию значения всякий раз, когда хотите получить к нему доступ. Хотя язык имеет синтаксический сахар для преобразования ms.MarshalJSON()
в (&ms).MarshalJSON()
в качестве способа доступа к методу с приемником указателя, это невозможно сделать для значения, хранящегося в переменной интерфейса. По этой причине метод не считается установленным в его методе.