json:源码解密序列化(二)

本文深入解析了Golang中JSON序列化的源码,从获取编码器方法、排序过滤到字段编码,详细介绍了核心逻辑和关键函数的作用,帮助理解内部实现机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

上一篇博客《json:你或许还不知道的使用的坑(一)》说了一些可能被误用的方式,主要是讲了结构体的匿名字段的使用。现在就分析下jsonMarshal,这部分代码整体逻辑比较简单,主要的流程有三几步:

  1. 反射获取结构体的每个字段的编码方法,如果字段是结构体就递归获取该字段结构体的每个编码器方法;
  2. 获取完之后对结构体的字段做一些排序,过滤(json的序列化一些处理规则,这里就是第一篇文章讲的一些莫名其妙的现象);
  3. 然后就调用每个字段的编码方法对每个字段进行编码;

一、获取编码器方法和排序、过滤

首先从json.Marshal这个入口方法进去看看主要逻辑:

func Marshal(v interface{
   }) ([]byte, error) {
   
	e := newEncodeState()

	err := e.marshal(v, encOpts{
   escapeHTML: true})
	if err != nil {
   
		return nil, err
	}
	buf := append([]byte(nil), e.Bytes()...)

	encodeStatePool.Put(e)

	return buf, nil
}

先忽略newEncodeState的作用,我们先关注主要逻辑,大致意思是序列化的结果会存在这个里面。
进入marshal去看看具体实现:

func (e *encodeState) marshal(v interface{
   }, opts encOpts) (err error) {
   
     ...
	e.reflectValue(reflect.ValueOf(v), opts)
	return nil
}

这部分代码也没有好说的,只是获得了序列化的反射对象。

func (e *encodeState) reflectValue(v reflect.Value, opts encOpts) {
   
	valueEncoder(v)(e, v, opts)
}

这部分代码的意思言简意赅,包括了前言讲的3个步骤,valueEncoder(v)获取了编码器方法,valueEncoder(v)(e, v, opts)就做编码操作获取结果。valueEncoder(v)里面就调用了typeEncoder(v.Type()),获取序列化对象的类型的每个字段类型的编码器方法,进去分析看看:

func typeEncoder(t reflect.Type) encoderFunc {
   
	if fi, ok := encoderCache.Load(t); ok {
   
		return fi.(encoderFunc)
	}
	var (
		wg sync.WaitGroup
		f  encoderFunc
	)
	// 防止多迭代情况下使用还没被初始化完成的f
	wg.Add(1)
	fi, loaded := encoderCache.LoadOrStore(t, encoderFunc(func(e *encodeState, v reflect.Value, opts encOpts) {
   
		wg.Wait()
		f(e, v, opts)
	}))
	if loaded {
   
		return fi.(encoderFunc)
	}
	// Compute the real encoder and replace the indirect func with it.
	f = newTypeEncoder(t, true)
	wg.Done()
	encoderCache.Store(t, f)
	return f
}

typeEncoder有优化逻辑在里面,只要获取了某个类型的编码器方法就会放到缓存里面,所有12行就使用wg.Wait()防止迭代情况下使用还没被初始化完成的编码器方法,必须等待19行真正获取到编码器方法才去编码。下面终于到了主题,进入到newTypeEncoder(t, true)

// 根据类型找到相应的类型编码器
func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
   
	// 非指针变量实现了Marshaler则
	if t.Kind() != reflect.Ptr && allowAddr && reflect.PtrTo(t).Implements(marshalerType) {
   
		// 不是指针但是实现了Marshaler
		return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false))
	}
	if t.Implements(marshalerType) {
   
		return marshalerEncoder
	}
	if t.Kind() != reflect.Ptr && allowAddr && reflect.PtrTo(t).Implements(textMarshalerType) {
   
		return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false))
	}
	if t.Implements(textMarshalerType) {
   
		return textMarshalerEncoder
	}
	switch t.Kind() {
   
	case reflect.Bool:
		return boolEncoder
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return intEncoder
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return uintEncoder
	case reflect.Float32:
		return float32Encoder
	case reflect.Float64:
		return float64Encoder
	case reflect.String:
		return stringEncoder
	case reflect.Interface:
		return interfaceEncoder
	case reflect.Struct:
		return newStructEncoder(t)
	case reflect.Map:
		return newMapEncoder(t)
	case reflect.Slice:
		return newSliceEncoder(t)
	case reflect.Array:
		return newArrayEncoder(t)
	case reflect.Ptr:
		return newPtrEncoder(t)
	default:
		return unsupportedTypeEncoder
	}
}

说明下上面代码的意思:

  1. 17~44行就是根据序列化值的类型,获得相应的编码器方法。所以可以知道当给类型取别名的时候是不支持的,除非自定义序列化。后面会着重分析newStructEncoder(t),其他的编码器方法会选择性分析。
  2. 第4行,当序列化对象的指针类型实现了Marshaler,但是此时序列化对象却是值类型,那么就最好获得序列化对象的地址,这样就可以调用自定义的MarshalerMarshalJSON方法,当然前提是该序列化对象是可以寻址的。如果是不能寻址的就会普通的获取到结构体的各个字段的编码器方法,此时自定义的序列化就无效了。这个坑在上篇文章有提到。
  3. 第8行判断该序列化对象是否实现了接口Marshaler,如果实现了接口就走自定义的序列化逻辑。序列化对象是指针的时候,会成功进入到这个判断。
  4. 11行~16行和上面的Marshaler逻辑差不多,只是这里是判断TextMarshaler接口,也是用来自定义序列化的。在上一篇文章也介绍过该自定义序列方式的使用。
  5. addrMarshalerEncodermarshalerEncoder主要逻辑就是调用自定义序列化方式。

下面分析newStructEncoder代码:当序列化对象的类型是结构体会往这里走:

func newStructEncoder(t reflect.Type) encoderFunc {
   
	se := structEncoder
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangshen023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值