draveness / blog-comments

面向信仰编程
https://draveness.me
140 stars 6 forks source link

Go 语言 JSON 的实现原理 | Go 语言设计与实现 · /golang-json #202

Closed draveness closed 2 years ago

draveness commented 4 years ago

https://draveness.me/golang-json

Go 语言 JSON 的实现原理

xiao7737 commented 4 years ago

对map中的int类型元素进行Marshal后,再进行 Unmarshal ,原来int类型就变成 float64 了。“通过 newTypeEncoder 选择到 intEncoder 进行Marshal” 这一步应该没错,是 Unmarshal 的时候出的问题吗?

func main() {
    u1 := map[string]interface{}{
        "Name": "tom",
        "Age":  18,
    }
    for k, v := range u1 {
        fmt.Printf("key:%v value:%v type of value:%T\n", k, v, v) //此处age是int
    }
    b, _ := json.Marshal(u1)
    var m map[string]interface{}
    _ = json.Unmarshal(b, &m)
    for k, v := range m {
        fmt.Printf("key:%v value:%v type of value:%T\n", k, v, v)  //此处的age是float64
    }
}
draveness commented 4 years ago

对map中的int类型元素进行Marshal后,再进行 Unmarshal ,原来int类型就变成 float64 了。“通过 newTypeEncoder 选择到 intEncoder 进行Marshal” 这一步应该没错,是 Unmarshal 的时候出的问题吗?

func main() {
  u1 := map[string]interface{}{
      "Name": "tom",
      "Age":  18,
  }
  for k, v := range u1 {
      fmt.Printf("key:%v value:%v type of value:%T\n", k, v, v) //此处age是int
  }
  b, _ := json.Marshal(u1)
  var m map[string]interface{}
  _ = json.Unmarshal(b, &m)
  for k, v := range m {
      fmt.Printf("key:%v value:%v type of value:%T\n", k, v, v)  //此处的age是float64
  }
}

JSON 的标准里其实只有 number 类型,float64 是 Go 做的统一转换,如果想转回 int 类型需要用 struct 给标准库一个提示用于反序列化。

image

xiao7737 commented 4 years ago

@draveness

对map中的int类型元素进行Marshal后,再进行 Unmarshal ,原来int类型就变成 float64 了。“通过 newTypeEncoder 选择到 intEncoder 进行Marshal” 这一步应该没错,是 Unmarshal 的时候出的问题吗?

func main() {
    u1 := map[string]interface{}{
        "Name": "tom",
        "Age":  18,
    }
    for k, v := range u1 {
        fmt.Printf("key:%v value:%v type of value:%T\n", k, v, v) //此处age是int
    }
    b, _ := json.Marshal(u1)
    var m map[string]interface{}
    _ = json.Unmarshal(b, &m)
    for k, v := range m {
        fmt.Printf("key:%v value:%v type of value:%T\n", k, v, v)  //此处的age是float64
    }
}

JSON 的标准里其实只有 number 类型,float64 是 Go 做的统一转换,如果想转回 int 类型需要用 struct 给标准库一个提示用于反序列化。

image

嗦嘎,int的编码器就将int变成number了是吧,也就是JavaScript 中的双精度浮点型格式,然后写入缓冲区

yuyongID commented 4 years ago

感觉 encodeState 和 decodeState 这个结构体非常重要的呀,序列化反序列的移位操作都被它的方法封装了,过程就不需要关心这些操作了。

yezuozuo commented 4 years ago

纠错:在不追求机制性能的情况下 =》 在不追求极致性能的情况下


2020-06-02 UPDATES: 已修复

yanping-li commented 3 years ago

看到stringEncoder,有一事不明,请教。 调用Marshal(v.String()),那不是又会回到这里?怎么退出的呢?

func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {
    if v.Type() == numberType {
        numStr := v.String()
        // In Go1.5 the empty string encodes to "0", while this is not a valid number literal
        // we keep compatibility so check validity after this.
        if numStr == "" {
            numStr = "0" // Number's zero-val
        }
        if !isValidNumber(numStr) {
            e.error(fmt.Errorf("json: invalid number literal %q", numStr))
        }
        e.WriteString(numStr)
        return
    }
    if opts.quoted {
        sb, err := Marshal(v.String())  <--- 这个地方是递归?那怎么退出呢?
        if err != nil {
            e.error(err)
        }
        e.string(string(sb), opts.escapeHTML)
    } else {
        e.string(v.String(), opts.escapeHTML)
    }
}
draveness commented 3 years ago

@yanping-li 看到stringEncoder,有一事不明,请教。 调用Marshal(v.String()),那不是又会回到这里?怎么退出的呢?

func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {
    if v.Type() == numberType {
        numStr := v.String()
        // In Go1.5 the empty string encodes to "0", while this is not a valid number literal
        // we keep compatibility so check validity after this.
        if numStr == "" {
            numStr = "0" // Number's zero-val
        }
        if !isValidNumber(numStr) {
            e.error(fmt.Errorf("json: invalid number literal %q", numStr))
        }
        e.WriteString(numStr)
        return
    }
    if opts.quoted {
        sb, err := Marshal(v.String())  <--- 这个地方是递归?那怎么退出呢?
        if err != nil {
            e.error(err)
        }
        e.string(string(sb), opts.escapeHTML)
    } else {
        e.string(v.String(), opts.escapeHTML)
    }
}

如果是递归,新的 string 在 Marshal 时的 option 只要 quoted = false 这里就一定会中止,看一下进入分支的条件

jzhahaha commented 3 years ago

请问一下 在marshal的时候在哪判断的私有变量呢? 通过反射应该是可以拿到私有变量的,但是在marshal的时候私有变量是不可导出的

draveness commented 3 years ago

@jzhahaha 请问一下 在marshal的时候在哪判断的私有变量呢? 通过反射应该是可以拿到私有变量的,但是在marshal的时候私有变量是不可导出的

从 typeFields 方法里看看

LiRonaldo commented 2 years ago

var ( wg sync.WaitGroup f encoderFunc ) wg.Add(1) fi, loaded := encoderCache.LoadOrStore(t, encoderFunc(func(e *encodeState, v reflect.Value, opts encOpts) { wg.Wait() f(e, v, opts) }))

大佬,抱歉,有个问题想问下,这个地方为啥要用waitgroup呢?