在一次需求开发过程中,需要在服务B中将服务A产生的一部分数据透传给用户,这部分数据结构由服务A和用户决定,服务B只负责透传,但是这部分数据需要被封装在结构体Msg中透传哥给用户。于是计划将透传字段设置为string类型。具体如下:
需要透传的结构为Track,由服务A和用户端确定。
type Track struct {
Id int `json:"num"`
name string `json:"name"`
}
服务B传给用户的结构体为Msg,其中 string 类型的track字段为透传信息。服务A以序列化后的json传递给服务B。
type Msg struct {
Id int `json:"id"`
Count int `json:"count"`
Track string `json:"track"`
}
服务端解析结构为UserMsg
type UserMsg struct {
Id int `json:"id"`
Count int `json:"count"`
Track Track `json:"track"`
}
为了验证工作流程,写了如下测试代码:
func TestJson(t *testing.T) {
var track = Track{
Id: 1,
name: "test",
}
tb,_ := json.Marshal(track)
track_str := string(tb)
msg := Msg{
Id:1,
Count: 10,
Track: track_str,
}
mb,_ := json.Marshal(msg)
fmt.Println(string(mb))
var umsg UserMsg
err := json.Unmarshal(mb,&umsg)
if err != nil{
fmt.Println(err.Error())
}
fmt.Printf("%+v",umsg)
}
结果显示:
{"id":1,"count":10,"track":"{"num":1}"}
json: cannot unmarshal string into Go struct field UserMsg.track of type json.Track
{Id:1 Count:10 Track:{Id:0 name:}}--- PASS: TestJson (0.00s)
我们发现这是由于B服务收到track信息时已经是序列化后的数据了。B服务在序列化结构体Msg时,track字段被序列化了两次,导致用户侧无法将按照string结构处理的track字段解析回来。当然,我们可以在收到track字段后先解序列化为Track结构,然后再统一按照UserMsg结构序列化,传递给用户。但这并不是一个好的解决方法,因此我们无需处理track字段中的任何信息,同时与服务A对接我们不需要处理的结构体会产能生不必要的工作量。另一方面将增加两个服务的耦合度。当服务A修改该结构体字段时,我们将需要花费时间去修改这部分内容。
那么是否存在一种方法可以使得透传的信息不被json进行二次编码呢?
json 提供了一种特殊的数据结构叫做RawMessage,这个类型本身是[]byte的别名,被声明为RawMessage类型的字段,在marshal和Unmarshal过程中将不被处理,因此如果我们将透传的信息存储在声明为RawMessage类型的字段中,就可以避免json的二次编码。按照这个思路,我们将测试代码修改如下:
type Track struct {
Id int `json:"num"`
name string `json:"name"`
}
type Msg struct {
Id int `json:"id"`
Count int `json:"count"`
Track json.RawMessage `json:"track"`
}
type UserMsg struct {
Id int `json:"id"`
Count int `json:"count"`
Track Track `json:"track"`
}
func TestJson(t *testing.T) {
var track = Track{
Id: 1,
name: "test",
}
tb,_ := json.Marshal(track)
msg := Msg{
Id:1,
Count: 10,
Track: tb,
}
mb,_ := json.Marshal(msg)
fmt.Println(string(mb))
var umsg UserMsg
err := json.Unmarshal(mb,&umsg)
if err != nil{
fmt.Println(err.Error())
}
fmt.Printf("%+v",umsg)
}
测试后发现问题圆满解决。