golang源码分析:easyjson(1)

2023-09-06 19:16:18 浏览数 (2)

https://github.com/mailru/easyjson另辟蹊径,它采用代码生成的方式,为每一个结构体生成对应的json序列化和反序列化方法,类似proto buf,由于是在编译时的代码生成,避免了运行时的内存分配和反射,所以效率比官方的json库快4到5倍。但是它丧失了灵活性,不支持未知类型的json的序列化和反序列化。下面我们来分析下它的源码。

首先配置环境,除了需要下载依赖的包外还需要下载代码生成工具

代码语言:javascript复制
go get github.com/mailru/easyjson && go install github.com/mailru/easyjson/...@latest

然后运行命令就可以生成结构体定制的序列化和反序列化方法,不过需要注意的是它不支持main 这个package里面的结构体代码的生成

代码语言:javascript复制
easyjson -all <file>.go

下面我们使用一个例子来生成代码:

代码语言:javascript复制
package model

import _ "github.com/mailru/easyjson/gen"

type Model struct {
  StringField string `json:"string_field"`
  IntField    int    `json:"int_field"`
  ObjectField struct {
    StringField string `json:"string_field"`
    IntField    int    `json:"int_field"`
  } `json:"object_field"`
  ArrayField []struct {
    StringField string `json:"string_field"`
    IntField    int    `json:"int_field"`
  } `json:"array_field"`
}

然后我们运行命令:

代码语言:javascript复制
 easyjson -all  ./json/easyjson/model/struct.go

可以看到,它生成了代码:json/easyjson/model/struct_easyjson.go

代码语言:javascript复制
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.

package model

import (
  json "encoding/json"
  easyjson "github.com/mailru/easyjson"
  jlexer "github.com/mailru/easyjson/jlexer"
  jwriter "github.com/mailru/easyjson/jwriter"
)

// suppress unused package warning
var (
  _ *json.RawMessage
  _ *jlexer.Lexer
  _ *jwriter.Writer
  _ easyjson.Marshaler
)

func easyjson9f2eff5fDecodeLearnJsonEasyjsonModel(in *jlexer.Lexer, out *Model) {
  isTopLevel := in.IsStart()
  if in.IsNull() {
    if isTopLevel {
      in.Consumed()
    }
    in.Skip()
    return
  }
  in.Delim('{')
  for !in.IsDelim('}') {
    key := in.UnsafeFieldName(false)
    in.WantColon()
    if in.IsNull() {
      in.Skip()
      in.WantComma()
      continue
    }
    switch key {
    case "string_field":
      out.StringField = string(in.String())
    case "int_field":
      out.IntField = int(in.Int())
    case "object_field":
      easyjson9f2eff5fDecode(in, &out.ObjectField)
    case "array_field":
      if in.IsNull() {
        in.Skip()
        out.ArrayField = nil
      } else {
        in.Delim('[')
        if out.ArrayField == nil {
          if !in.IsDelim(']') {
            out.ArrayField = make([]struct {
              StringField string `json:"string_field"`
              IntField    int    `json:"int_field"`
            }, 0, 2)
          } else {
            out.ArrayField = []struct {
              StringField string `json:"string_field"`
              IntField    int    `json:"int_field"`
            }{}
          }
        } else {
          out.ArrayField = (out.ArrayField)[:0]
        }
        for !in.IsDelim(']') {
          var v1 struct {
            StringField string `json:"string_field"`
            IntField    int    `json:"int_field"`
          }
          easyjson9f2eff5fDecode(in, &v1)
          out.ArrayField = append(out.ArrayField, v1)
          in.WantComma()
        }
        in.Delim(']')
      }
    default:
      in.SkipRecursive()
    }
    in.WantComma()
  }
  in.Delim('}')
  if isTopLevel {
    in.Consumed()
  }
}
func easyjson9f2eff5fEncodeLearnJsonEasyjsonModel(out *jwriter.Writer, in Model) {
  out.RawByte('{')
  first := true
  _ = first
  {
    const prefix string = ","string_field":"
    out.RawString(prefix[1:])
    out.String(string(in.StringField))
  }
  {
    const prefix string = ","int_field":"
    out.RawString(prefix)
    out.Int(int(in.IntField))
  }
  {
    const prefix string = ","object_field":"
    out.RawString(prefix)
    easyjson9f2eff5fEncode(out, in.ObjectField)
  }
  {
    const prefix string = ","array_field":"
    out.RawString(prefix)
    if in.ArrayField == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
      out.RawString("null")
    } else {
      out.RawByte('[')
      for v2, v3 := range in.ArrayField {
        if v2 > 0 {
          out.RawByte(',')
        }
        easyjson9f2eff5fEncode(out, v3)
      }
      out.RawByte(']')
    }
  }
  out.RawByte('}')
}

// MarshalJSON supports json.Marshaler interface
func (v Model) MarshalJSON() ([]byte, error) {
  w := jwriter.Writer{}
  easyjson9f2eff5fEncodeLearnJsonEasyjsonModel(&w, v)
  return w.Buffer.BuildBytes(), w.Error
}

// MarshalEasyJSON supports easyjson.Marshaler interface
func (v Model) MarshalEasyJSON(w *jwriter.Writer) {
  easyjson9f2eff5fEncodeLearnJsonEasyjsonModel(w, v)
}

// UnmarshalJSON supports json.Unmarshaler interface
func (v *Model) UnmarshalJSON(data []byte) error {
  r := jlexer.Lexer{Data: data}
  easyjson9f2eff5fDecodeLearnJsonEasyjsonModel(&r, v)
  return r.Error()
}

// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *Model) UnmarshalEasyJSON(l *jlexer.Lexer) {
  easyjson9f2eff5fDecodeLearnJsonEasyjsonModel(l, v)
}
func easyjson9f2eff5fDecode(in *jlexer.Lexer, out *struct {
  StringField string `json:"string_field"`
  IntField    int    `json:"int_field"`
}) {
  isTopLevel := in.IsStart()
  if in.IsNull() {
    if isTopLevel {
      in.Consumed()
    }
    in.Skip()
    return
  }
  in.Delim('{')
  for !in.IsDelim('}') {
    key := in.UnsafeFieldName(false)
    in.WantColon()
    if in.IsNull() {
      in.Skip()
      in.WantComma()
      continue
    }
    switch key {
    case "string_field":
      out.StringField = string(in.String())
    case "int_field":
      out.IntField = int(in.Int())
    default:
      in.SkipRecursive()
    }
    in.WantComma()
  }
  in.Delim('}')
  if isTopLevel {
    in.Consumed()
  }
}
func easyjson9f2eff5fEncode(out *jwriter.Writer, in struct {
  StringField string `json:"string_field"`
  IntField    int    `json:"int_field"`
}) {
  out.RawByte('{')
  first := true
  _ = first
  {
    const prefix string = ","string_field":"
    out.RawString(prefix[1:])
    out.String(string(in.StringField))
  }
  {
    const prefix string = ","int_field":"
    out.RawString(prefix)
    out.Int(int(in.IntField))
  }
  out.RawByte('}')
}

里面实现了两个函数:

代码语言:javascript复制
func (v Model) MarshalJSON() ([]byte, error) {
  w := jwriter.Writer{}
  easyjson9f2eff5fEncodeLearnJsonEasyjsonModel(&w, v)
  return w.Buffer.BuildBytes(), w.Error
}
代码语言:javascript复制
func (v *Model) UnmarshalJSON(data []byte) error {
  r := jlexer.Lexer{Data: data}
  easyjson9f2eff5fDecodeLearnJsonEasyjsonModel(&r, v)
  return r.Error()
}

它们内部调用的函数,就是生成的定制化的序列化方法和反序列化方法。进一步我们可以看到,序列户方法就是依次取结构体的各个字段,然后拼接json字符串。

代码语言:javascript复制
func easyjson9f2eff5fEncodeLearnJsonEasyjsonModel(out *jwriter.Writer, in Model) {
  out.RawByte('{')
  first := true
  _ = first
  {
    const prefix string = ","string_field":"
    out.RawString(prefix[1:])
    out.String(string(in.StringField))
  }

反序列化方式类似,通过lexer解析json数据,然后遍历json抽象语法树,根据jsonkey名字,给结构体对应的字段赋值:

代码语言:javascript复制
func easyjson9f2eff5fDecodeLearnJsonEasyjsonModel(in *jlexer.Lexer, out *Model) {
  isTopLevel := in.IsStart()
  if in.IsNull() {
    if isTopLevel {
      in.Consumed()
    }
    in.Skip()
    return
  }
  in.Delim('{')
  for !in.IsDelim('}') {
    key := in.UnsafeFieldName(false)
    in.WantColon()
    if in.IsNull() {
      in.Skip()
      in.WantComma()
      continue
    }
    switch key {
    case "string_field":
      out.StringField = string(in.String())
    case "int_field":
      out.IntField = int(in.Int())
    case "object_field":
      easyjson9f2eff5fDecode(in, &out.ObjectField)

我们如何使用这些序列化和反序列化方法呢?

代码语言:javascript复制
package main

import (
  "fmt"
  "learn/json/easyjson/model"

  "github.com/mailru/easyjson"
)

func main() {
  someStruct := &model.Model{StringField: "12", IntField: 12, ObjectField: struct {
    StringField string `json:"string_field"`
    IntField    int    `json:"int_field"`
  }{}}
  rawBytes, err := easyjson.Marshal(someStruct)
  fmt.Println(string(rawBytes), err)
  someStruct1 := &model.Model{}
  err = easyjson.Unmarshal(rawBytes, someStruct1)
  fmt.Println(err)
}

我们可以进一步跟进 easyjson.Marshal和 easyjson.Unmarshal 源码位于github.com/mailru/easyjson@v0.7.7/helpers.go

代码语言:javascript复制
func Marshal(v Marshaler) ([]byte, error) {
  if isNilInterface(v) {
    return nullBytes, nil
  }

  w := jwriter.Writer{}
  v.MarshalEasyJSON(&w)
  return w.BuildBytes()
}
代码语言:javascript复制
func Unmarshal(data []byte, v Unmarshaler) error {
  l := jlexer.Lexer{Data: data}
  v.UnmarshalEasyJSON(&l)
  return l.Error()
}

可以看到,它仅仅是个封装,使得序列化和反序列化方法和官方方法一样。内部调用了传入go对象的 MarshalEasyJSON和UnmarshalEasyJSON 方法。

至此,easyjson的基本原理和使用已经介绍完毕,下一讲,我们分析下easyjson的代码生成的源码。

0 人点赞