由reflect.New()制成的切片的JSON元帅在golang中给出null

时间:2018-11-01 07:41:48

标签: json go reflect

我正在golang中使用reflect创建新的初始化切片。但是,当我json.Marshal这个新的反射切片时,我得到一个JSON null值而不是[]。请看这里的例子,在这里我比较两种情况:

package main

import (
  "encoding/json"
  "reflect"
  "log"
)
func makeslice(slice interface{}) interface{} {

  return reflect.New(reflect.TypeOf(slice)).Elem().Interface()
}
func main() {

  s0 := []int{2,3,5}

  s1 := makeslice(s0).([]int)
  js1,e1 := json.Marshal(s1)
  log.Println("case 1:",reflect.TypeOf(s1),reflect.ValueOf(s1),e1,string(js1))

  s2 := []int{}
  js2,e2 := json.Marshal(s2)
  log.Println("case 2:",reflect.TypeOf(s2),reflect.ValueOf(s2),e2,string(js2))

}

这给出了输出:

case 1: []int [] <nil> null
case 2: []int [] <nil> []

请注意,case 1case 2会产生完全相同的日志输出,除了最终的json字符串外,第一种情况显示null,第二种情况显示[]

为什么会这样?我想要的是让case 1显示[],因为我朋友的客户端应用程序始终期望一个数组,并且零长度数组不应显示为null。

我在做什么错了?

1 个答案:

答案 0 :(得分:6)

reflect.New()按照documentation的作用:

  

New返回一个值,该值表示指向指定类型的新零值的指针。也就是说,返回的值的类型为PtrTo(typ)。

基本上,它将创建新数据,其值是该类型的零值。 []int的零值为nil

在您的代码中,s0s2都不为零。但是s1将是[]int类型的零值(即nil),因为它是使用reflect.New()创建的。

下面的代码证明切片零值为nil

var a []int = []int{}
log.Printf("%#v \n", a) // ====> []int{}

var b []int
log.Printf("%#v \n", b) // ====> []int(nil)

要制作新的切片,请使用reflect.MakeSlice()

func makeslice(slice interface{}) interface{} {
    return reflect.MakeSlice(reflect.TypeOf(slice), 0, 0).Interface()
}

然后根据您的代码,输出将是:

case 1: []int [] <nil> []
case 2: []int [] <nil> []

工作场所:https://play.golang.org/p/tL3kFqVwOtC


将具有[]int值的nil转换为JSON将得到null。但是将[]int{}数据转换为JSON将产生[],因为数据是空片(而不是nil片)。


作为评论的后续内容,此方法会生成可寻址的切片,可以通过反射进一步修改:

func makeslice(slice interface{}) interface{} {
    newsliceval := reflect.MakeSlice(reflect.TypeOf(slice),0,0)
    newslice := reflect.New(newsliceval.Type()).Elem()
    newslice.Set(newsliceval)

    /*
     * make any desired changes to newslice with reflect package
     */

    return newslice.Interface()
}

更多说明在这里 Why golang reflect.MakeSlice returns un-addressable Value