Golang json.Decoder 和 json.Unmarshal分析

1,239 阅读2分钟

前言

在我一直的认知中,golang的json.Decoder就是对数据解码,而json.Unmarshal是对数据的反序列化,两者是完全不同的东西。今天在读文章时惊奇的发现两者其实是一样的

image.png

这两个方法都是golang将数据从易传输的二进制字节数组转换为我们可读的不同类型的数据的实现。但是两者又略有不同:

  • Use json.Decoder if your data is coming from an io.Reader stream, or you need to decode multiple values from a stream of data. For the case of reading from an HTTP request, I’d pick json.Decoder since you’re obviously reading from a stream.

  • Use json.Unmarshal if you already have the JSON data in memory.

json.Decoder解码

看看源码

//我们通常通过调用NewDecoder()函数初始化一个Decoder句柄
func NewDecoder(r io.Reader) *Decoder {
   return &Decoder{r: r}
}

通过初始化拿到的*Decoder一般接下来就是去调用Decode()方法

func (dec *Decoder) Decode(v any) error {
   //一系列条件判断,这里我们不做分析
   ...
   
   // Don't save err from unmarshal into dec.err:
   // the connection is still usable since we read a complete JSON
   // object from it before the error happened.
   
   err = dec.d.unmarshal(v)       //这里

   ...

   return err
}

我们发现Decode()方法其实就是去调用了底层的unmarshal()方法,那我们就再去看看unmarshal()方法

func (d *decodeState) unmarshal(v any) error {
   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
}

通过源码我们看到,通过反射获取我们传入数据的reflect.ValueOf,然后将其传入value()方法实现数据的反序列化,看看json.Decoder的应用

func HandleUse(w http.ResponseWriter, r *http.Request) {
    var u Use
    if err := json.NewDecoder(r.Body).Decode(&u); err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    w.WriteHeader(http.StatusOK)
    fmt.Fprintf(w, "姓名:%s,年龄:%d", u.Name, u.Age)

}

json.Unmarshal反序列化

先看源码

func Unmarshal(data []byte, v any) error {
   // Check for well-formedness.
   // Avoids filling out half a data structure
   // before discovering a JSON syntax error.
   var d decodeState
   err := checkValid(data, &d.scan)   //校验检查
   if err != nil {
      return err
   }

   d.init(data)
   return d.unmarshal(v)   //这里
}

我们发现json.Unmarshal其实也是去调用了底层的unmarshal()方法,与Decode()类似。再看json.Unmarshal的应用

func HandleUse(w http.ResponseWriter, r *http.Request) {
    var u Use //此处的Use是一个结构体
    data, err := ioutil.ReadAll(r.Body)//此处的r是http请求得到的json格式数据-->然后转化为[]byte格式数据.
    if err != nil {
        w.WriteHeader(http.StatusBadRequest)
        return
    }
    if err := json.Unmarshal(data, &u); err != nil { //经过这一步将json解码赋值给结构体,由json转化为结构体数据
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    w.WriteHeader(http.StatusOK)
    fmt.Fprintf(w, "姓名:%s,年龄:%d", u.Name, u.Age)
}

总结

json.Decoder会一个一个元素进行加载,不会把整个json数组读到内存里面,适用于从数据流中解码多个值。例如http连接与socket连接的读取与写入,或者是文件的读取

json.Unmarshal适用于读取已经在内存中的json数据进行解码,例如直接是[]byte的输入

image.png