关于go:为什么json.Unmarshal为什么使用引用而不使用指针?

关于go:为什么json.Unmarshal为什么使用引用而不使用指针?

type ShortTranscodeEntity struct {
	Weight    int            `json:"weight"`    // 任务权重
	TaskId    string         `json:"taskId"`    //任务id
	TaskName  string         `json:"taskName"`  //视频标题
	TransType int            `json:"transType"` //转码类型   0 轮播 1点播
	Map       map[string]int `json:"map"`
	Arr       []int          `json:"arr"`
}

var shortMark = `{
"weight": 100,
"taskId": "task001",
"taskName": "example video",
"streamId": "stream001"
}`

func Test_Unmarshal(t *testing.T) {
	var f ShortTranscodeEntity
	t.Log(f)                                               // {0   0}
	t.Log(&f)                                              // &{0   0}
	t.Logf("&f的类型 %T &f占用的字节数是 %d", &f, unsafe.Sizeof(&f)) //  &f的类型 *feature.ShortTranscodeEntity &f占用的字节数是 8
	f.Map["1"] = 1
	f.Arr = append(f.Arr, 1)
	err := json.Unmarshal([]byte(shortMark), &f)
	if err != nil {
		t.Logf("unmarshal f have some err:%v", err)
		return
	}
	t.Log(f) // {100 task001 example video 0}

	var pf *ShortTranscodeEntity
	t.Log(pf)                                              // <nil>
	t.Logf("pf的类型 %T pf占用的字节数是 %d", pf, unsafe.Sizeof(pf)) // pf的类型 *feature.ShortTranscodeEntity pf占用的字节数是 8
	err = json.Unmarshal([]byte(shortMark), pf)
	if err != nil {
		t.Logf("unmarshal pf have some err:%v", err)
		return
	}
	t.Log(pf)
}

现象

定义指针 unmarshal 时,出现 json: Unmarshal(nil *feature.ShortTranscodeEntity)错误。

原因

指针初始化值为 nil, 结构体初始化为基础值。

json.unmarshal源码中

func (d *decodeState) unmarshal(v any) error 
// 当 v 为 nil 是,返回 Value{}, 此时
	rv := reflect.ValueOf(v)
	if rv.Kind() != reflect.Pointer || rv.IsNil() {
		return &InvalidUnmarshalError{reflect.TypeOf(v)}
	}

	d.scan.reset()
	d.scanWhile(scanSkipSpace)
	// We decode rv not rv.Elem because the Unmarshaler interface
	// test must be applied at the top level of the value.
	err := d.value(rv)
	if err != nil {
		return d.addErrorContext(err)
	}
	return d.savedError
}

// ValueOf 函数返回一个新的 Value,该 Value 初始化为接口 i 中存储的具体值。
// ValueOf(nil) 返回零值 Value。
// ValueOf returns a new Value initialized to the concrete value
// stored in the interface i. ValueOf(nil) returns the zero Value.
func ValueOf(i any) Value {
	if i == nil {
		return Value{}
	}

	// TODO: Maybe allow contents of a Value to live on the stack.
	// For now we make the contents always escape to the heap. It
	// makes life easier in a few places (see chanrecv/mapassign
	// comment below).
	escapes(i)

	return unpackEface(i)
}

// IsNil reports whether its argument v is nil. The argument must be
// a chan, func, interface, map, pointer, or slice value; if it is
// not, IsNil panics. Note that IsNil is not always equivalent to a
// regular comparison with nil in Go. For example, if v was created
// by calling ValueOf with an uninitialized interface variable i,
// i==nil will be true but v.IsNil will panic as v will be the zero
// Value.
// IsNil报告其参数v是否为nil。
// 参数必须是chan、func、interface、map、pointer或slice值;否则,IsNil将会panic。
// 请注意,IsNil并不总是等价于在Go中与nil进行常规比较。
// 例如,如果v是通过调用ValueOf并传入未初始化的接口变量i创建的,那么i==nil将为true,但v.IsNil将会panic,因为v是零值。
func (v Value) IsNil() bool {
	k := v.kind()
	switch k {
	case Chan, Func, Map, Pointer, UnsafePointer:
		if v.flag&flagMethod != 0 {
			return false
		}
		ptr := v.ptr
		if v.flag&flagIndir != 0 {
			ptr = *(*unsafe.Pointer)(ptr)
		}
		return ptr == nil
	case Interface, Slice:
		// Both interface and slice are nil if first word is 0.
		// Both are always bigger than a word; assume flagIndir.
		return *(*unsafe.Pointer)(v.ptr) == nil
	}
	panic(&ValueError{"reflect.Value.IsNil", v.kind()})
}

因为指针初始化为 nil 所以返回 json: Unmarshal(nil *feature.ShortTranscodeEntity) 错误

arr map 呢?

func Test_Unmarshal_slice(t *testing.T) {
	markArr := `[1,2,3]`

	var arr []int
	t.Log(arr)                                                     // []
	t.Log(&arr)                                                    // &[]
	t.Logf("&arr的类型 %T &arr占用的字节数是 %d", &arr, unsafe.Sizeof(&arr)) // &arr的类型 *[]int &arr占用的字节数是 8

	err := json.Unmarshal([]byte(markArr), &arr)
	if err != nil {
		t.Log(err)
		return
	}
	t.Log(arr)

	var parr *[]int
	t.Log(parr)                                                      // <nil>
	t.Log(parr)                                                      // <nil>
	t.Logf("&parr的类型 %T &parr占用的字节数是 %d", parr, unsafe.Sizeof(parr)) // parr的类型 *[]int parr占用的字节数是 8

	err = json.Unmarshal([]byte(markArr), parr)
	if err != nil {
		t.Log(err) // json: Unmarshal(nil *[]int)
		return
	}
	t.Log(parr)
}

go 的值传递。

结论

json unmarshal 时,注意要初始化数据,谨慎使用指针。

map, slice 初始化为 nil。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值