1.Quiz

有如下一个例子:

package mainimport ("encoding/json""fmt""time"
)type RecordBrief struct {time.TimeID int
}func main() {r := RecordBrief{Time: time.Now(),ID:   6,}m, _ := json.MarshalIndent(r, "", "\t")fmt.Println(string(m))
}

你期望的结果是像:

{"Time": "2022-06-25T10:49:39.597537249+08:00","ID": 6
}

还是:

{"ID": 6
}

或者是别的?


2.Answer

其实如果你认为的答案不是:

"2022-06-25T10:52:23.590933959+08:00"

也没能想明白原因,可以继续往下看看。


3.Resolving

诚然,我们在学习json的序列化和反序列化的时候,目的就是把一个Golang struct值序列化为对应的json string罢了。可能我们还知道一些Marshal的规则,比如struct的字段需要定义为可导出的,比如还可通过定义对应的json tag来修改struct field对应的json字段名称等。

但是对于json.Marshal函数的细节可能大家不会去太在意。本次提出的问题中,我们不难注意到其中的time.Time是一个匿名(Anonymous)字段,而这个就是答案的由来。我们先看看json.Marshal的注释文档中的一个解释:

// ...
// Marshal traverses the value v recursively.
// If an encountered value implements the Marshaler interface
// and is not a nil pointer, Marshal calls its MarshalJSON method
// to produce JSON. If no MarshalJSON method is present but the
// value implements encoding.TextMarshaler instead, Marshal calls
// its MarshalText method and encodes the result as a JSON string.
// ...
func Marshal(v interface{}) ([]byte, error) {...
}

Marshal函数递归地遍历传入的序列化对象v(及其成员)。当面对一个实现了json.Marshaler接口的对象(不能是一个空指针)时,Marshal函数就会调用该对象的MarshalJSON方法来生成JSON内容。如果没有实现json.Marshaler,而是实现了encoding.TextMarshaler接口,那么就会调用它的MarshalText方法,然后把该方法返回的结果转编为一个JSON字符串。

然后我们再看看time.Time

type Time struct {...
}// MarshalJSON implements the json.Marshaler interface.
// The time is a quoted string in RFC 3339 format, with sub-second precision added if present.
func (t Time) MarshalJSON() ([]byte, error) {if y := t.Year(); y < 0 || y >= 10000 {// RFC 3339 is clear that years are 4 digits exactly.// See golang.org/issue/4556#c15 for more discussion.return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")}b := make([]byte, 0, len(RFC3339Nano)+2)b = append(b, '"')b = t.AppendFormat(b, RFC3339Nano)b = append(b, '"')return b, nil
}

所以time.Time是实现了json.Marshaler接口的。然后观察到它的实现是把时间按照RFC3339Nano格式字符串值返回为json序列化结果,这和我们实际上运行程序看到的结果是一致的。

那么再看看我们的type定义:

type RecordBrief struct {time.TimeID int
}

为什么ID字段不见了?正是因为匿名字段的原因,Golang中的这种用法有点类似于继承,所以RecordBrief类型也自动具有了time.Time的所有方法,当然也包括了MarshalJSON,从而也就实现了json.Marshaler接口。如此一来,当一个RecordBrief被Marshal的时候,它的序列化结果就被time.Time的序列化结果给覆盖了。


Conclusion

如果你和我一样,没能一下知道原因,那多半是对一些常见的知识了解的深度和广度不够。我之前确实不知道time.Time居然也实现了json.Marshaler接口,也不清楚json.Marshal到底在做什么,所以不知道答案也就理所当然了,后来经过阅读文档注释,才终于对该问题有了一些认知(后续应该总结一篇json.Marshal的源码解析)。

至此,如果我们想要这种样子的结果:

{"Time": "2022-06-25T10:49:39.597537249+08:00","ID": 6
}

最简单的方式是修改struct,将time.Time作为一个非匿名的导出字段:

type RecordBrief struct {Time time.TimeID int
}

另一种方法是给我们的RecordBrief实现json.Marshaler接口:

type RecordBrief struct {time.TimeID int
}func (r RecordBrief) MarshalJSON() ([]byte, error) {//非常简单的一种方式就是创建中间类型t := struct {Time time.TimeID   int}{r.Time,r.ID,}return json.Marshal(t)
}

TO BE CONTINUE…

[Golang] json.Marshal问题总结相关推荐

  1. golang json Marshal遇到的字符转义

    使用json.Marshal 如果字符串中含有 < > &字符会出现自动转义问题 不太符合预期输出 原始字符 转义后 &         \u0026 < \u003 ...

  2. Golang json 中文marshal

    今天遇到了坑, golang 直接json.Marshal 中文 客户端不解析.必须是ASCII码才可以,所以学习了下,示例如下: package mainimport ("encoding ...

  3. 【golang程序包推荐分享】分享亿点点golang json操作及myJsonMarshal程序包开发的踩坑经历 :)

    目录[阅读时间:约5分钟] 一.概述 1.Json的作用 2.Go官方 encoding/json 包 3. golang json的主要操作 二.Json Marshal:将数据编码成json字符串 ...

  4. golang json 获取所有key_Golang —— JSON 大法

    「每一个程序员都无法逃脱 JSON 的命运魔爪」 JSON 简直就是一个神奇的玩意,只要是人类可以阅读的数据结构,基本都可以转成 JSON 的数据格式,其在各个平台.组件.模块中穿梭不止,使用上更是游 ...

  5. golang json数组拼接

    2016年06月16日 15:38:25 阅读数:2575 标签: golang json 数组  更多 个人分类: golang func main() {a := []byte(`{"P ...

  6. byte用json存 c++_玩转golang——JSON高性能自动字段名

    前言 golang最近在中国非常火爆,尤其是后端服务开发场景.原生并发支持.优秀的性能.统一的风格,极大提升了开发效率.笔者用golang独立开发过不少小中型系统,写了几万行代码,确实很爽. 不过,统 ...

  7. go nil json.marshal 完是null_字节跳动踩坑记#3:Go服务灵异panic

    这个坑比较新鲜,刚填完,还冒着冷气. - 1 - 在字节跳动,我们服务的所有 log 都通过统一的日志库采集到流式日志服务.落地 ES 集群,配上字节云超(sang)级(xin)强(bing)大(ku ...

  8. 再测Golang JSON类库

    写项目一直需要进行序列化,听到了,也看到了很多同学老师对各个golang的json库进行测评.那本人为什么还要继续进行这一次测评呢? 因为实践过的知识最有说服力,也是属于自己的,我也希望看到本博文的同 ...

  9. Golang json 解析与生成

    文章目录 1.解析 json 1.1 map[string]interface{} 存储 json 1.2 struct 存储 json 1.3 []map[string]interface{} 解析 ...

最新文章

  1. HTML做frame跳转设置响应头,X-Frame-Options header响应头如何配置
  2. java equals() 函数_java equals()函数与‘==’
  3. 连接远程应用服务器appserver出错,Worklight 6.2:无法使用WL.Client.Connect连接到Worklight App Server...
  4. 如何在XenDesktop中映射USB设备
  5. centos经常用到的一些文件
  6. 2018-2019-2 20165221 【网络对抗技术】-- Exp6 信息搜集与漏洞扫描
  7. 传统量化与ai量化对比_量化AI偏差的风险
  8. [面试] 步步为营:吉大学士的PG宝洁面经
  9. 小程序跳转样式布局错乱_微信小程序页面布局问题
  10. 怀化市哪里有学计算机的,怀化汽修学校推荐-哪里好,计算机职业学校
  11. 2-2 Aruba控制器 无线漫游优化 2020
  12. 一个是阆苑仙葩,一个是美玉无瑕
  13. ICTCLAS的Python3实现
  14. 镜子中的 Google
  15. 【自动控制原理仿真实验】 稳定性及稳态误差实验(实验三)
  16. MySQL 架构与 SQL 执行流程
  17. 【破解利器】PE工具篇(PE编辑,查看等操作)
  18. 孩子不是两人婚姻的砝码
  19. 这是烙印,是成长的陪伴,是归属
  20. mysql写入一句话木马

热门文章

  1. 程序员才看得懂的祝福语(程序员送祝福啦)
  2. edg击败we视频_2017LPL春季赛4月8日WE VS EDG视频:EDG 2:0 WE获胜
  3. java模拟三人爬山_java笔记——模拟多人徒步爬山例子
  4. Mat对象使用at<uchar>报错
  5. Shell编程中的数组定义、遍历
  6. 单模多模还傻傻分不清楚?关于光模块介绍,看这一篇就够啦!
  7. OSI 七层模型和TCP/IP模型及对应协议(详解)
  8. 项目在使用easyui时遇到的问题
  9. Python math库函数
  10. java jtextarea 事件_JTextArea事件处理