Problem Introduction
JSON serializes a struct if it has a field string or []byte type but actually holds the data in JSON format, such as
type Message struct { From string `json:"from"` To string `json:"to"` Data string `json:"data"`}func main() { msg := Message{ From: "XiaoMing", To: "LiGang", Data: `{"title":"test","body":"something"}`, } jsonData, err := json.Marshal(msg) if err != nil { panic(err) } fmt.Println(string(jsonData))}
In the above example, the data field is of type string, but the content is stored in JSON format, and at this point the program outputs:
{"from":"XiaoMing","to":"LiGang","data":"{\"title\":\"test\",\"body\":\"something\"}"}
As you can see, the serialized data is a string.
If the message corresponds to a table in the database, and the data field is JSON type in the database, when we need an interface, query the records in the message table to return to the client. If serialization is performed directly, the data that the client obtains is actually a string, and the client also needs to JSON deserialize the string itself.
At this point we wonder, is there any way to serialize the data field to a JSON object instead of a string when the server serializes the message?
Custom serialization
Because the value of the data field is itself a JSON type, why can't it be used directly at serialization time?
View the official documentation for the JSON package and we can find examples of custom serialization
When performing JSON serialization, if the corresponding type implements the Marshaler
interface:
type Marshaler interface { MarshalJSON() ([]byte, error)}
The method is then executed MarshalJSON
, and the returned byte array is used as the serialized value of the value.
Then back to the example above, we can easily achieve the goal:
type JsonString stringfunc (j JsonString) MarshalJSON() ([]byte, error) { fmt.Println("marshal...") return []byte(j), nil}type Message struct { From string `json:"from"` To string `json:"to"` Data JsonString `json:"data"`}
The above code, based on the string
type declaration JsonString
, represents a JSON-formatted string, and implements the marshaler interface. Because jsonstring represents a JSON string, it is directly converted to a byte array to return.
The data field in the message is then replaced with the jsonstring type.
To execute the program again, you can see:
{"from":"XiaoMing","to":"LiGang","data":{"title":"test","body":"something"}}
perfect!