Golang中使用JSON序列化进行深度相等性测试有哪些陷阱

来源:建站作者:松松建站头衔:草根站长
导读:本期聚焦于小伙伴创作的《Golang中使用JSON序列化进行深度相等性测试有哪些陷阱》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《Golang中使用JSON序列化进行深度相等性测试有哪些陷阱》有用,将其分享出去将是对创作者最好的鼓励。

在Golang开发中,判断两个复杂数据结构是否完全相等是常见需求,部分开发者会选择先将两个结构序列化为JSON字符串再比较字符串是否一致的方式实现深度相等性测试,这种方式看似便捷,实际存在多个容易被忽略的陷阱。

Golang中使用JSON序列化进行深度相等性测试有哪些陷阱

陷阱一:数值类型序列化后精度丢失

JSON的规范中数值类型没有区分整数和浮点数,当结构中存在大整数或者特定浮点数时,序列化后可能出现精度变化,导致比较结果不符合预期。

比如下面的示例,int64类型的大整数在JSON序列化后转回字符串时,部分场景下会出现精度丢失:

package main

import (
	"encoding/json"
	"fmt"
)

type Data struct {
	Num int64
}

func main() {
	a := Data{Num: 9223372036854775807}
	b := Data{Num: 9223372036854775807}

	aBytes, _ := json.Marshal(a)
	bBytes, _ := json.Marshal(b)

	fmt.Println(string(aBytes) == string(bBytes)) // 部分场景下可能返回false
}

陷阱二:零值和空值的处理差异

结构中的零值和空值在JSON序列化时的表现不同,会导致相等性判断出错。比如指针类型的零值是nil,切片类型的零值是nil,而空切片序列化后的结果和nil序列化后的结果并不一致。

以下示例展示了nil切片和空切片的序列化差异:

package main

import (
	"encoding/json"
	"fmt"
)

type List struct {
	Items []int
}

func main() {
	a := List{Items: nil}
	b := List{Items: []int{}}

	aBytes, _ := json.Marshal(a)
	bBytes, _ := json.Marshal(b)

	fmt.Println(string(aBytes)) // 输出 {"Items":null}
	fmt.Println(string(bBytes)) // 输出 {"Items":[]}
	fmt.Println(string(aBytes) == string(bBytes)) // 输出 false
}

陷阱三:无法处理循环引用结构

如果待比较的结构中存在循环引用,JSON序列化时会直接抛出错误,无法完成后续的比较操作。比如一个节点结构指向自己,序列化时就会陷入无限递归。

package main

import (
	"encoding/json"
	"fmt"
)

type Node struct {
	Val  int
	Next *Node
}

func main() {
	a := &Node{Val: 1}
	a.Next = a // 循环引用

	_, err := json.Marshal(a)
	fmt.Println(err) // 输出 json: unsupported value: encountered a cycle via *main.Node
}

陷阱四:字段顺序影响比较结果

Go的json.Marshal方法序列化结构体时,字段顺序是按照结构体定义顺序排列的,但如果待比较的两个结构是通过不同方式生成的map类型,map的遍历顺序是不确定的,序列化后的JSON字符串顺序也会不同,导致字符串比较返回false。

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	a := map[string]int{"x": 1, "y": 2}
	b := map[string]int{"y": 2, "x": 1}

	aBytes, _ := json.Marshal(a)
	bBytes, _ := json.Marshal(b)

	// 两次运行可能得到不同的顺序,比较结果不确定
	fmt.Println(string(aBytes) == string(bBytes))
}

陷阱五:私有字段和自定义序列化逻辑干扰

结构体中的私有字段不会被JSON序列化,如果结构依赖私有字段区分相等性,那么JSON序列化方式会忽略这部分信息。另外如果结构体实现了MarshalJSON方法,自定义了序列化逻辑,也可能导致序列化后的结果和实际的相等性判断逻辑不符。

package main

import (
	"encoding/json"
	"fmt"
)

type User struct {
	Name   string
	age    int // 私有字段,不会被序列化
}

func main() {
	a := User{Name: "test", age: 10}
	b := User{Name: "test", age: 20}

	aBytes, _ := json.Marshal(a)
	bBytes, _ := json.Marshal(b)

	fmt.Println(string(aBytes) == string(bBytes)) // 返回 true,但实际两个结构不相等
}

更合适的深度相等性测试方案

Go标准库的reflect包提供了DeepEqual方法,可以直接比较两个值是否深度相等,避免了JSON序列化的各类陷阱。不过DeepEqual也有自身的注意点,比如它会比较类型是否完全一致,零值和空值的判断更符合Go的语义。

package main

import (
	"fmt"
	"reflect"
)

type List struct {
	Items []int
}

func main() {
	a := List{Items: nil}
	b := List{Items: []int{}}

	fmt.Println(reflect.DeepEqual(a, b)) // 输出 false,符合预期
}

总结来说,使用JSON序列化进行深度相等性测试只适合非常简单的、无循环引用、无特殊类型、无私有字段的场景,大多数业务场景下更推荐使用reflect.DeepEqual,或者根据业务需求自定义相等性判断逻辑,避免落入上述陷阱。

GolangJSON序列化深度相等性测试reflect_DeepEqual修改时间:2026-07-01 04:09:37

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。