自定义MarshalJSON()在Go中不会被调用

时间:2021-11-25 08:09:02

I've written custom versions of MarshalJSON and UnmarshalJSON. My UnmarshalJSON gets called the way I want it to, but I can't get it to work with MarshalJSON. Here's code that summarizes my problem:

我已经编写了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"
}

In short, the program encodes the struct "automatically" in two ways, and then finally calls MarshalJSON manually. The response I want is "charlie". Running the code generates the following output:

简而言之,程序以两种方式“自动”编码结构体,然后最终手动调用MarshalJSON。我想要的回应是“查理”。运行代码生成以下输出:

{"data":"bravo"}
{"data":"bravo"}
{"data":"charlie"}

Try it at Go Playground: http://play.golang.org/p/SJ05S8rAYN

在Go游乐场试试:http://play.golang.org/p/SJ05S8rAYN

1 个解决方案

#1


29  

In this part of the code, ms gets copied into an interface{} variable:

在这部分代码中,ms被复制到接口{}变量:

// 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).

问题是这个变量没有实现json。封送器接口,因为封送器json不在myStruct的方法集中(仅用于*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).

修复的方法是(a)使您的MarshalJSON方法接受一个非指针接收器(这意味着它将获得结构的副本:如果它是大型的,可能代价很高),或者(b)将一个指向结构的指针(如Kavu在注释中提到的)。

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.

这种行为的原因是Go不允许您获取存储在接口变量中的值的指针,而是要求您在访问该值时复制该值。虽然这种语言有语法上的优势,可以将marshaljson()转换为(&ms). marshaljson(),作为使用指针接收器访问方法的一种方式,但不能对存储在接口变量中的值进行这种转换。由于这个原因,该方法不被认为在其方法集中。

#1


29  

In this part of the code, ms gets copied into an interface{} variable:

在这部分代码中,ms被复制到接口{}变量:

// 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).

问题是这个变量没有实现json。封送器接口,因为封送器json不在myStruct的方法集中(仅用于*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).

修复的方法是(a)使您的MarshalJSON方法接受一个非指针接收器(这意味着它将获得结构的副本:如果它是大型的,可能代价很高),或者(b)将一个指向结构的指针(如Kavu在注释中提到的)。

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.

这种行为的原因是Go不允许您获取存储在接口变量中的值的指针,而是要求您在访问该值时复制该值。虽然这种语言有语法上的优势,可以将marshaljson()转换为(&ms). marshaljson(),作为使用指针接收器访问方法的一种方式,但不能对存储在接口变量中的值进行这种转换。由于这个原因,该方法不被认为在其方法集中。