首先上代码:

func main() {    b := true    a1, _ := json.Marshal(b)    a2, _ := Marshal(b)    fmt.Println(string(a1))    fmt.Println(string(a2))}

输出:

truetrue

以最简单的单个bool 类型的序列化为例,我们来搞清楚json 包里面的调用栈如下:

从入口出发,第一个方法是:

func Marshal(v interface{}) ([]byte, error) {    e := &encodeState{}    err := e.marshal(v)    if err != nil {        return nil, err    }    return e.Bytes(), nil}

这里没有什么特殊,只是为了引出encodeState

// An encodeState encodes JSON into a bytes.Buffer.type encodeState struct {    bytes.Buffer // accumulated output    scratch      [64]byte}

这里的Buffer就是我们所有输出字节的Buffer,再继续往里面走

func (e *encodeState) marshal(v interface{}) (err error) {    defer func() {        if r := recover(); r != nil {            if _, ok := r.(runtime.Error); ok {                panic(r)            }            if s, ok := r.(string); ok {                panic(s)            }            err = r.(error)        }    }()    e.reflectValue(reflect.ValueOf(v))    return nil}

这里我们看到了反射获得值,个人认为这里的命名不太科学,不过无妨,继续往下走。

func valueEncoder(v reflect.Value) encoderFunc {    if !v.IsValid() {        return invalidValueEncoder    }    return typeEncoder(v.Type())}

这里通过反射的类型,获得和使用该类型的序列化方法.顺便还做了把缓存。

func typeEncoder(t reflect.Type) encoderFunc {    encoderCache.RLock()    f := encoderCache.m[t]    encoderCache.RUnlock()    if f != nil {        return f    }    // To deal with recursive types, populate the map with an    // indirect func before we build it. This type waits on the    // real func (f) to be ready and then calls it.  This indirect    // func is only used for recursive types.    encoderCache.Lock()    if encoderCache.m == nil {        encoderCache.m = make(map[reflect.Type]encoderFunc)    }    var wg sync.WaitGroup    wg.Add(1)    encoderCache.m[t] = func(e *encodeState, v reflect.Value, quoted bool) {        wg.Wait()        f(e, v, quoted)    }    encoderCache.Unlock()    // Compute fields without lock.    // Might duplicate effort but won't hold other computations back.    f = newTypeEncoder(t, true)    wg.Done()    encoderCache.Lock()    encoderCache.m[t] = f    encoderCache.Unlock()    return f}

至于具体的Encoder,通过Type很容易找到。

switch t.Kind() {    case reflect.Bool:        return boolEncoder    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:        return intEncoder    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:        return uintEncoder    case reflect.Float32:        return float32Encoder    case reflect.Float64:        return float64Encoder    case reflect.String:        return stringEncoder    case reflect.Interface:        return interfaceEncoder    case reflect.Struct:        return newStructEncoder(t)    case reflect.Map:        return newMapEncoder(t)    case reflect.Slice:        return newSliceEncoder(t)    case reflect.Array:        return newArrayEncoder(t)    case reflect.Ptr:        return newPtrEncoder(t)    default:        return unsupportedTypeEncoder

最终我们这里的boolEncoder的实现

func boolEncoder(e *encodeState, v reflect.Value, quoted bool) {    if quoted {        e.WriteByte('"')    }    if v.Bool() {        e.WriteString("true")    } else {        e.WriteString("false")    }    if quoted {        e.WriteByte('"')    }}

当然这个只是一个最简单的示例,里面很多实现的细节可以推敲。这里只是把这种序列化的方式归结为类型反射+缓存的方式。

备注

这个系列不是一个大而全的package api 指南,只包括作者认为最常见的使用方式。抗议无效。

golang json 包简单分析的更多相关文章

  1. [golang] 抓包注入分析

    视频信息 Packet Capture, Analysis, and Injection with Goby John Leonat GopherCon 2016 https://www.youtub ...

  2. golang flag包简单例子

    package main import ( "flag" "fmt" ) var workers int; func main() { flag.IntVar( ...

  3. Golang的json包

    encoding/json encoding/json是官方提供的标准json, 实现RFC 7159中定义的JSON编码和解码.使用的时候需要预定义struct,原理是通过reflection和in ...

  4. Golang fmt包使用小技巧

    h1 { margin-top: 0.6cm; margin-bottom: 0.58cm; direction: ltr; color: #000000; line-height: 200%; te ...

  5. 易企秀H5 json配置文件解密分析

    最近需要参考下易企秀H5的json配置文件,发现已经做了加密,其实前端的加密分析起来只是麻烦点. 抓包分析 先看一个H5: https://h5.eqxiu.com/s/XvEn30op F12可以看 ...

  6. 开源网络抓包与分析框架学习-Packetbeat篇

    开源简介packbeat是一个开源的实时网络抓包与分析框架,内置了很多常见的协议捕获及解析,如HTTP.MySQL.Redis等.在实际使用中,通常和Elasticsearch以及kibana联合使用 ...

  7. Golang测试包

    Golang测试包 golang自带了测试包(testing),直接可以进行单元测试.性能分析.输出结果验证等.简单看着官方文档试了试,总结一下: 目录结构和命令 使用golang的测试包,需要遵循简 ...

  8. Golang JSON操作汇总

    直接把结构体编码成json数据 package main import ( "encoding/json" "fmt" _ "os" ) t ...

  9. Golang : pflag 包简介

    笔者在前文中介绍了 Golang 标准库中 flag 包的用法,事实上有一个第三方的命令行参数解析包 pflag 比 flag 包使用的更为广泛.pflag 包的设计目的就是替代标准库中的 flag ...

随机推荐

  1. python操作memcached以及分布式

    memcached 是以 LiveJournal 旗下 Danga Interactive 公司的 Brad Fitzpatric 为首开发的一款软件.现在已成为 mixi.Facebook.Live ...

  2. 应用HTK搭建语音拨号系统4: 识别器评估

    选自:http://maotong.blog.hexun.com/6261890_d.html 苏统华 哈尔滨工业大学人工智能研究室 2006年10月30日 声明:版权所有,转载请注明作者和来源 该系 ...

  3. Linux Vsftpd 连接超时解决方法

    Linux Vsftpd 连接超时解决方法 2013-11-13 10:58:34|  分类: 默认分类|举报|字号 订阅     解决方法(http://www.lingdus.com/thread ...

  4. docker -v挂载数据卷网络异常的问题

    docker 删除容器并重新运行容器时报如下异常: docker: Error response from daemon: failed to create endpoint tomcat001 on ...

  5. eclipse 工作环境配置

    1.更换编辑颜色, http://eclipse-color-theme.github.io/update/ 下载离线安装包,解压缩 eclipse-color-theme-update-site\u ...

  6. Linux下查看某一进程所占用内存的方法

    Linux下查看某一个进程所占用的内存,首先可以通过ps命令找到进程id,比如 ps -ef | grep kafka 可以看到kafka这个程序的进程id 可以看到是2913,现在可以使用如下命令查 ...

  7. Lock+Condition 相对于 wait+notify 的一个优势案例分析

    问题的描述 启动3个线程打印递增的数字, 线程1先打印1,2,3,4,5, 然后是线程2打印6,7,8,9,10, 然后是线程3打印11,12,13,14,15. 接着再由线程1打印16,17,18, ...

  8. springMVC配置文件位置及名称

    在web.xml文件内配置springMVC的DispatcherServlet的那个servlet内添加 <servlet> <servlet-name>mvc</se ...

  9. Python 开发轻量级爬虫02

    Python 开发轻量级爬虫 (imooc总结02--爬虫简介) 爬虫简介 首先爬虫是什么?它是一段自动抓取互联网信息的程序. 什么意思呢? 互联网由各种各样的的网页组成,每一个网页都有对应的url, ...

  10. C字符串和指针问题汇总

    空指针和传参问题 1) 段错误.形参改为二级指针即可 void GetMemory( char *p ){ p = ( ); } void Test( void ){ char *str = NULL ...