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

JSON marshal of slice made from reflect.New() gives null in golang

我在 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。

我做错了什么?

reflect.New() 按照 documentation 做了什么:

New returns a Value representing a pointer to a new zero value for the specified type. That is, the returned Value's Type is PtrTo(typ).

在您的代码中,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() 来代替切片。生成的值不会是 nil.

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


将值 nil[]int 转换为 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.