
Go 的 json 包在编码/解码指针时会解引用并复制值,导致原本指向同一对象的指针在反序列化后变为指向独立副本——这是符合规范的预期行为,而非 bug。
go 的 `json` 包在编码/解码指针时会解引用并复制值,导致原本指向同一对象的指针在反序列化后变为指向独立副本——这是符合规范的预期行为,而非 bug。
在 Go 中,encoding/json 对指针的处理是语义导向而非内存导向:它不保留原始指针地址,而是将 *T 视为“可选的 T 值”,编码时写入其解引用后的值(即 *ptr),解码时则分配新的内存空间来存储该值,并让指针指向这个新地址。因此,即使原始结构中 n.A[0] 和 n.B[0] 指向同一个 J 实例,经 json.Marshal → 文件保存 → json.Unmarshal 流程后,它们将分别指向两个内容相同、但地址不同的 J 实例。
这正是你观察到 n.A[0] == n.B[0] 从 true 变为 false 的根本原因:== 在指针类型上比较的是内存地址,而 JSON 反序列化不重建原始地址关系。
✅ 正确的等价性判断方式是比较值本身:
// ✅ 比较解引用后的结构体内容(要求 J 是可比较类型,如无 map/slice/func 字段)
if *n.A[0] == *n.B[0] {
fmt.Println("same logical value")
}
// ✅ 或使用 reflect.DeepEqual(适用于任意结构,包括含不可比较字段的情况)
if reflect.DeepEqual(n.A[0], n.B[0]) {
fmt.Println("deeply equal")
}
⚠️ 注意事项:
-
json包不支持序列化函数、channel、map(作为键)、unsafe.Pointer 等非 JSON 可表示类型; - 若
J包含不可比较字段(如map[string]int),则*n.A[0] == *n.B[0]编译失败,此时必须用reflect.DeepEqual; - 如需保持指针图谱(如图结构、循环引用),JSON 并非合适格式;应考虑
gob(Go 原生二进制格式)或自定义序列化逻辑(例如用 ID 映射 + 后置重建指针); - 示例代码中
Open函数存在类型不一致问题:var n NeuralNetwork与返回*N不匹配,应统一为var n N并返回&n,或直接声明var n N后返回&n(注意NeuralNetwork是否为N别名)。
总结:这不是缺陷,而是 JSON 作为语言无关数据交换格式的设计取舍。若业务逻辑依赖对象身份(identity)而非值相等(equality),请避免依赖指针相等性判断,并选用更适合的序列化方案或显式维护引用映射。











