news 2026/4/27 23:38:24

Go 语言从入门到进阶 | 第 11 章:编码与序列化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Go 语言从入门到进阶 | 第 11 章:编码与序列化

系列:Go 语言从入门到进阶
作者:耿雨飞
适用版本:go v1.26.2


前置条件

在开始本章学习之前,请确保:

  • 已完成第 10 章的学习,熟悉io.Reader/io.Writer接口及其组合方式
  • 理解结构体、接口、方法等 Go 核心概念
  • 已获取 Go 1.26.2 源码树(go-go1.26.2目录)

导读

序列化(Serialization)是将内存中的数据结构转换为可存储或传输的字节流的过程;反序列化(Deserialization)是其逆过程。Go 标准库提供了丰富的编码包,涵盖 JSON、XML、CSV、Gob、Binary 等格式,它们统一建立在io.Reader/io.Writer接口之上,与上一章学习的 I/O 体系无缝衔接。

本章将系统学习这些编码工具。我们从最常用的 JSON 处理出发,深入结构体标签机制和自定义编解码接口,然后扩展到 XML、CSV、Gob 和 binary 格式,最后学习强大的模板引擎。

本章将对照 Go 1.26.2 源码中的以下关键路径:

源码路径内容说明
src/encoding/json/encode.goJSON 编码核心:MarshalMarshaler接口
src/encoding/json/decode.goJSON 解码核心:UnmarshalUnmarshaler接口
src/encoding/json/stream.go流式编解码:EncoderDecoder
src/encoding/json/tags.go结构体标签解析
src/encoding/xml/marshal.goXML 编码与标签规则
src/encoding/xml/xml.goXML 解析器 token 类型定义
src/encoding/csv/reader.goCSV 读取器
src/encoding/csv/writer.goCSV 写入器
src/encoding/gob/doc.goGob 格式说明
src/encoding/binary/binary.go底层二进制编解码
src/text/template/doc.go模板语法完整说明
src/html/template/doc.goHTML 安全模板

学习目标

学完本章后,你应当能够:

  1. 使用encoding/json完成 Go 值与 JSON 之间的编解码,理解类型映射规则
  2. 熟练运用结构体标签(json:"field,omitempty")控制 JSON 字段行为
  3. 实现MarshalJSON/UnmarshalJSON接口自定义复杂类型的编解码逻辑
  4. 使用Encoder/Decoder进行流式 JSON 处理
  5. 掌握encoding/xml的基本操作和标签规则
  6. 了解encoding/csvencoding/gobencoding/binary的使用场景
  7. 使用text/templatehtml/template进行数据驱动的文本生成

11.1 JSON 处理

JSON(JavaScript Object Notation)是当今最流行的数据交换格式。Go 的encoding/json包提供了完整的 JSON 支持,API 设计围绕两个核心函数——Marshal(编码)和Unmarshal(解码)。

11.1.1encoding/json:编码与解码

Marshal——Go 值到 JSON

Marshal将任意 Go 值递归编码为 JSON 字节切片:

// src/encoding/json/encode.go(第 205 行)funcMarshal(v any)([]byte,error){e:=newEncodeState()deferencodeStatePool.Put(e)err:=e.marshal(v,encOpts{escapeHTML:true})iferr!=nil{returnnil,err}buf:=append([]byte(nil),e.Bytes()...)returnbuf,nil}

源码洞察Marshal使用sync.PoolencodeStatePool)复用编码状态对象,避免频繁分配。编码完成后通过append([]byte(nil), ...)创建结果的独立副本,确保 Pool 回收不影响返回值。默认启用 HTML 转义(escapeHTML: true),会将<>&转义为\u003c\u003e\u0026

Go 类型到 JSON 的映射规则:

Go 类型JSON 类型说明
boolbooleantrue/false
int,float64等数值numberNaN/Inf 会返回错误
stringstring自动转义非法 UTF-8
[]bytestringbase64 编码
structobject导出字段成为 key
map[string]Tobjectkey 必须是字符串或实现TextMarshaler
[]T,[n]Tarraynil slice 编码为null
*T(非 nil)递归编码指针指向的值
*T(nil)null
any(nil)null

基本使用示例:

packagemainimport("encoding/json""fmt")typeUserstruct{Namestring`json:"name"`Ageint`json:"age"`Emailstring`json:"email,omitempty"`}funcmain(){user:=User{Name:"Alice",Age:30}// 编码data,err:=json.Marshal(user)iferr!=nil{fmt.Println("编码错误:",err)return}fmt.Println(string(data))// {"name":"Alice","age":30}// 带缩进的编码pretty,_:=json.MarshalIndent(user,""," ")fmt.Println(string(pretty))// {// "name": "Alice",// "age": 30// }}

Unmarshal——JSON 到 Go 值

Unmarshal解析 JSON 数据并存储到目标值中:

// src/encoding/json/decode.go(第 102 行)funcUnmarshal(data[]byte,v any)error{vard decodeState err:=checkValid(data,&d.scan)iferr!=nil{returnerr}d.init(data)returnd.unmarshal(v)}

解码之前会先做完整的语法验证(checkValid),确保不会在解码到一半时才发现语法错误。

JSON 到 Go 的映射规则(解码到any接口时):

JSON 类型Go 类型
booleanbool
numberfloat64
stringstring
array[]any
objectmap[string]any
nullnil

解码到结构体时,字段匹配规则:

  1. 优先匹配 JSON tag 指定的名称
  2. 其次匹配字段名(大小写不敏感)
  3. 未匹配的 JSON 字段被忽略(除非使用Decoder.DisallowUnknownFields
  4. 多个字段匹配同一 key 时,精确匹配优先于大小写不敏感匹配
packagemainimport("encoding/json""fmt")funcmain(){jsonStr:=`{"name":"Bob","age":25,"email":"bob@example.com"}`varuser User err:=json.Unmarshal([]byte(jsonStr),&user)iferr!=nil{fmt.Println("解码错误:",err)return}fmt.Printf("%+v\n",user)// {Name:Bob Age:25 Email:bob@example.com}// 解码到 mapvarmmap[string]any json.Unmarshal([]byte(jsonStr),&m)fmt.Println(m["name"],m["age"])// Bob 25(age 是 float64)}

流式编解码——Encoder 与 Decoder

当数据来源于io.Reader或需要写入io.Writer时,应使用流式 API:

// src/encoding/json/stream.go// Decoder 从 io.Reader 读取并解码 JSONtypeDecoderstruct{r io.Reader buf[]byted decodeState scanpint// 未读数据起始位置scannedint64// 已扫描的总字节数// ...}// Encoder 编码 JSON 并写入 io.WritertypeEncoderstruct{w io.Writer errerrorescapeHTMLbool// ...}

Decoder的优势:

  • 无需将整个 JSON 数据一次性加载到内存
  • 支持UseNumber()将数字解码为json.Number(字符串形式),避免精度丢失
  • 支持DisallowUnknownFields()拒绝结构体中没有对应字段的 JSON key
packagemainimport("encoding/json""fmt""os""strings")funcmain(){// Decoder:从 Reader 解码input:=`{"name":"Charlie","age":35}`+"\n"+`{"name":"Diana","age":28}`dec:=json.NewDecoder(strings.NewReader(input))fordec.More(){varuser Useriferr:=dec.Decode(&user);err!=nil{break}fmt.Printf("%+v\n",user)}// Encoder:编码到 Writerenc:=json.NewEncoder(os.Stdout)enc.SetIndent(""," ")enc.SetEscapeHTML(false)// 禁用 HTML 转义enc.Encode(User{Name:"Eve",Age:22})}

Encoder.Encode会在每个 JSON 值后追加一个换行符(\n),这使得 NDJSON(Newline Delimited JSON)格式的数据可以自然地逐条写入和读取。

json.RawMessage——延迟解析

json.RawMessage类型是[]byte的别名,实现了MarshalerUnmarshaler接口。它允许在解码时保留 JSON 原始内容,稍后再决定如何解析:

typeEventstruct{Typestring`json:"type"`Data json.RawMessage`json:"data"`// 延迟解析}funcmain(){raw:=`{"type":"user","data":{"name":"Frank","age":40}}`varevent Event json.Unmarshal([]byte(raw),&event)// 根据 type 决定如何解析 dataswitchevent.Type{case"user":varuser User json.Unmarshal(event.Data,&user)fmt.Printf("User: %+v\n",user)}}

11.1.2 结构体标签(json:"field"

结构体标签是 Go 编码包的核心机制,它通过反射读取字段上的元数据来控制编解码行为。

标签解析逻辑在src/encoding/json/tags.go中:

// parseTag 将 tag 按第一个逗号拆分为名称和选项funcparseTag(tagstring)(string,tagOptions){tag,opt,_:=strings.Cut(tag,</
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/27 23:33:44

数能与能量:在比特与原子之间重寻世界的本源

当我们凝视手中的智能手机&#xff0c;或是惊叹于生成式人工智能瞬间创作出的画作时&#xff0c;我们究竟看到了什么&#xff1f;是冰冷的硅基芯片&#xff0c;还是流淌的光电&#xff1f;在很长一段时间里&#xff0c;我们习惯于用“人工智能”这个词来定义这个时代的技术奇迹…

作者头像 李华
网站建设 2026/4/27 23:28:29

卫生间沉箱回填,这3个关键点很少人告诉你

上个月去一个别墅工地巡检&#xff0c;正好赶上卫生间沉箱回填。工人正往坑里倒碎砖头、水泥块&#xff0c;我当场就叫停了。项目经理还跟我说“没事&#xff0c;大家都这么干”。我说&#xff0c;你们这么干&#xff0c;以后漏水了谁负责&#xff1f;沉箱回填这事儿&#xff0…

作者头像 李华
网站建设 2026/4/27 23:20:55

暗黑破坏神2存档编辑器:d2s-editor完全指南

暗黑破坏神2存档编辑器&#xff1a;d2s-editor完全指南 【免费下载链接】d2s-editor 项目地址: https://gitcode.com/gh_mirrors/d2/d2s-editor 还在为暗黑破坏神2漫长的刷装备过程感到疲惫吗&#xff1f;想要快速体验不同职业build却不想从头练级&#xff1f;d2s-edit…

作者头像 李华
网站建设 2026/4/27 23:19:57

计算机网络系统安装的结构逻辑、施工重点与运维价值

一、什么是计算机网络系统安装&#xff1f;计算机网络系统安装&#xff0c;是指在办公楼、工业厂房、园区、学校、医院、数据机房、商业综合体以及各类建筑空间中&#xff0c;通过网络设备、传输线路、机柜系统、配线系统、服务器接入、安全设备、无线覆盖和管理平台等一整套工…

作者头像 李华