一、应用
json.RawMessage
应用在判断情况是否解析,以及解析到什么结构上。例如:
type People struct {
Age uint8 `json:"age"`
Source uint8 `json:"source"`
Journey json.RawMessage `json:"journey"`
}
情境一:不解析。查看18岁及以上的人,行程里是否去过北京。那么对于Age < 18
岁的人就不必解析Journey
情景二:解析到异构。国家要统一行程数据,但是之前各省都开发了自己的软件,字段不统一。那汇总的时候Journey
应该是根据Source
来源去分别解析到几个不同的结构体上去判别,而那些不需要的字段,不必从里边解析出来。
二、源码
// RawMessage is a raw encoded JSON value.
// It implements Marshaler and Unmarshaler and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawMessage []byte
// MarshalJSON returns m as the JSON encoding of m.
func (m RawMessage) MarshalJSON() ([]byte, error) {
if m == nil {
return []byte("null"), nil
}
return m, nil
}
// UnmarshalJSON sets *m to a copy of data.
func (m *RawMessage) UnmarshalJSON(data []byte) error {
if m == nil {
return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
}
*m = append((*m)[0:0], data...)
return nil
}
var _ Marshaler = (*RawMessage)(nil)
var _ Unmarshaler = (*RawMessage)(nil)
可以看出json.RawMessage
是[]byte
的重定义,分别实现了Marshaler
接口和Unmarshaler
接口,在json化的时候什么都不做,在反json化的时候只对源数据拷贝,也不做什么。
我觉得特别值得学习的有两点 :
-
编译期检测写法。
var _ Marshaler = (*RawMessage)(nil)
,断言*RawMessage
实现了Marshaler
接口 -
空间重利用。
*m = append((*m)[0:0], data ...)
。这个其实大多数情况下没什么用,是说对RawMessage
重新反Json化的时候,从底层数组原地址开始重写。 举例:var p People data := []byte(`{ "likes": ["琴","棋"] }`) json.Unmarshal(data, &p) likes := p.Likes // 记录第一次有值时此处数据 fmt.Printf("解析2个。起始地址:%p,初始字符串:%s, 字符串:%s \n", &p.Likes[0], string(likes), string(p.Likes)) data = []byte(`{ "likes": ["书"] }`) json.Unmarshal(data, &p) fmt.Printf("改为1个。起始地址:%p,初始字符串:%s, 字符串:%s \n", &p.Likes[0], string(likes), string(p.Likes)) data = []byte(`{ "likes": ["画","诗","酒","茶"] }`) json.Unmarshal(data, &p) fmt.Printf("改为3个。起始地址:%p,初始字符串:%s, 字符串:%s \n", &p.Likes[0], string(likes), string(p.Likes))
解析2个。起始地址:0xc000012340,初始字符串:["琴","棋"], 字符串:["琴","棋"] 改为1个。起始地址:0xc000012340,初始字符串:["书"]"棋"], 字符串:["书"] 改为3个。起始地址:0xc00001a100,初始字符串:["书"]"棋"], 字符串:["画","诗","酒","茶"]
可以看到,第二次改为更短的内容,地址不会改变,重写了前边部分。第三次改成更长的,原有空间不够,使用新地址。
如果不这么写,可以试着把源码复制过来,修改
*m = append((*m)[0:0], data...)
为*m = append([]byte{}, data...)
,修改Journey json.RawMessage
为Journey RawMessage
,可以发现地址一直是变化的,a是不变的。