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
を2つの方法で「自動的に」エンコードし、最後にMarshalJSON
を手動で呼び出します。私が欲しい応答は"charlie"
。コードを実行すると、次の出力が生成されます。
{"data":"bravo"}
{"data":"bravo"}
{"data":"charlie"}
Go Playgroundでお試しください: http://play.golang.org/p/SJ05S8rAYN
コードのこの部分では、ms
がinterface{}
変数にコピーされます。
// Trying another method (UNSUCCESSFUL)
if ret, err := json.Marshal(ms); err != nil {
問題は、MarshalJSON
がmyStruct
の- method set にないため(json.Marshaler
の場合のみ)、この変数が*myStruct
インターフェースを実装しないことです。
修正は、(a)MarshalJSON
メソッドに非ポインターレシーバーを取得させる(つまり、構造体のコピーを取得することを意味します:構造体のコピーは、サイズが大きい場合はコストがかかる可能性があります)、または(b)ポインターをマーシャリングします。構造体に(Kavuがコメントで述べたように)。
この動作の理由は、Goではインターフェイス変数内に格納されている値へのポインターを取得できないため、アクセスするたびに値のコピーを作成する必要があるためです。言語には、ポインターレシーバーを使用してメソッドにアクセスする方法としてms.MarshalJSON()
を(&ms).MarshalJSON()
に変換する構文シュガーがありますが、これは、インターフェイス変数に格納されている値に対しては実行できません。このため、メソッドはそのメソッドセットに含まれているとは見なされません。