3

我在 golang 中使用反射来制作一个新的初始化切片。但是当我 json.Marshal 这个新的反射切片时,我得到一个 JSONnull值而不是一个[]. 在此处查看此示例,其中我比较了两种情况:

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显示的是[]因为我朋友的客户端应用程序总是需要一个数组,并且零长度数组不应该显示为空。

我究竟做错了什么?

4

1 回答 1

7

reflect.New()根据文档做什么:

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

在您的代码中,两者s0都不s2是零。但是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


将value 转换[]intnilJSON 将产生null. 但是将[]int{}数据转换为 JSON 会导致[]因为数据是空切片(不是零切片)。


作为评论的后续,此方法生成一个可寻址切片,可以使用反射进一步修改:

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()
}

更多解释 为什么 golang reflect.MakeSlice 返回不可寻址的 Value

于 2018-11-01T07:58:29.240 回答