从json.RawMessage学到的东西

一、应用

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.RawMessageJourney RawMessage ,可以发现地址一直是变化的,a是不变的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值