
本文探讨了在go语言中如何实现json字段的单向处理,即允许字段从json反序列化(读取)但阻止其在序列化(写入)时出现。针对 `json:"-"` 标签无法满足此需求的问题,文章提出了一种有效的结构体分离策略。通过定义两个语义不同的结构体——一个用于内部完整数据表示,另一个用于外部公共数据传输——可以灵活控制字段的序列化行为,从而实现敏感信息的保护和api接口的精简。
在Go语言的Web服务开发中,我们经常需要处理结构体与JSON数据之间的转换。一个常见的需求是,某个结构体字段在从JSON反序列化(读取)时需要被填充,但在序列化(写入)为JSON响应时却需要被忽略,例如用户密码哈希值、内部密钥等敏感信息。直接使用 json:"-" 标签虽然可以阻止字段被序列化和反序列化,但它无法满足“只读不写”的单向需求。本文将详细介绍一种推荐的解决方案:结构体分离策略。
考虑以下 User 结构体,其中 PasswordHash 字段存储了用户的密码哈希值。我们希望在接收用户注册或更新请求时能够从JSON中读取 PasswordHash,但在向客户端返回用户信息时,该字段不应出现在JSON响应中。
type User struct {
UserName string `json:"userName"`
Projects []string `json:"projects"`
PasswordHash string `json:"passwordHash,omitempty"` // 尝试使用omitempty,但仍会序列化
IsAdmin bool `json:"isAdmin"`
}如果我们将 PasswordHash 字段标记为 json:"-",它将在所有JSON操作中被忽略,无论是 json.Unmarshal 还是 json.Marshal。这与我们的“读取但跳过写入”的需求相悖。
type User struct {
UserName string `json:"userName"`
Projects []string `json:"projects"`
PasswordHash string `json:"-"` // 导致无法从JSON中读取PasswordHash
IsAdmin bool `json:"isAdmin"`
}显然,json:"-" 标签无法实现这种单向控制。
立即学习“go语言免费学习笔记(深入)”;
解决此问题的最佳实践是,将输入(反序列化)和输出(序列化)视为不同的语义对象。这意味着为内部数据模型和外部API接口定义不同的结构体。
具体来说,我们可以定义两个结构体:
下面是修改后的结构体定义:
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
)
// UserInfo 结构体包含可公开的用户信息,用于API响应
type UserInfo struct {
UserName string `json:"userName"` // 必须唯一
Projects []string `json:"projects"` // 用户有权访问的项目集合
IsAdmin bool `json:"isAdmin"` // 用户是否为管理员
}
// User 结构体包含完整的用户数据,包括敏感信息,用于内部存储和反序列化
type User struct {
UserInfo // 嵌入UserInfo,继承其字段
// PasswordHash 字段不带JSON标签,默认会进行反序列化
// 但在序列化UserInfo时会被忽略
PasswordHash string `json:"passwordHash,omitempty"` // 仅用于反序列化,或在内部使用时方便
}通过结构体分离,我们可以轻松实现单向的JSON处理。
当从JSON内容读取数据时,我们依然将数据反序列化到完整的 User 结构体中。由于 UserInfo 被嵌入到 User 中,并且 PasswordHash 字段没有 json:"-" 标签,json.Unmarshal 会正确地填充所有字段。
func main() {
// 模拟接收到的JSON内容
content := []byte(`{
"userName": "john.doe",
"projects": ["projectA", "projectB"],
"passwordHash": "some_super_secret_hash_value",
"isAdmin": true
}`)
var user User
err := json.Unmarshal(content, &user)
if err != nil {
log.Fatalf("反序列化失败: %v", err)
}
fmt.Println("--- 反序列化结果 (User 结构体) ---")
fmt.Printf("UserName: %s\n", user.UserName)
fmt.Printf("Projects: %v\n", user.Projects)
fmt.Printf("PasswordHash: %s\n", user.PasswordHash) // PasswordHash 被成功读取
fmt.Printf("IsAdmin: %t\n", user.IsAdmin)
fmt.Println()
}当需要将数据序列化为JSON响应时,我们只序列化 User 结构体中的 UserInfo 部分。这样,PasswordHash 字段就会被自动忽略,因为它不属于 UserInfo 结构体。
func main() {
// ... (接上面的反序列化代码) ...
// 假设我们现在要将这个用户对象发送给客户端
// 我们只序列化其公共信息部分 (UserInfo)
userBytes, err := json.MarshalIndent(user.UserInfo, "", " ") // 注意这里是 user.UserInfo
if err != nil {
log.Fatalf("序列化失败: %v", err)
}
fmt.Println("--- 序列化结果 (UserInfo 结构体) ---")
fmt.Println(string(userBytes))
// 输出将不包含 "passwordHash" 字段
}完整的示例代码如下:
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
)
// UserInfo 结构体包含可公开的用户信息,用于API响应
type UserInfo struct {
UserName string `json:"userName"` // 必须唯一
Projects []string `json:"projects"` // 用户有权访问的项目集合
IsAdmin bool `json:"isAdmin"` // 用户是否为管理员
}
// User 结构体包含完整的用户数据,包括敏感信息,用于内部存储和反序列化
type User struct {
UserInfo // 嵌入UserInfo,继承其字段
// PasswordHash 字段不带JSON标签,默认会进行反序列化
// 但在序列化UserInfo时会被忽略
PasswordHash string `json:"passwordHash"` // 确保反序列化时能正确匹配
}
func main() {
// 模拟接收到的JSON内容
content := []byte(`{
"userName": "john.doe",
"projects": ["projectA", "projectB"],
"passwordHash": "some_super_secret_hash_value",
"isAdmin": true
}`)
var user User
err := json.Unmarshal(content, &user)
if err != nil {
log.Fatalf("反序列化失败: %v", err)
}
fmt.Println("--- 反序列化结果 (User 结构体) ---")
fmt.Printf("UserName: %s\n", user.UserName)
fmt.Printf("Projects: %v\n", user.Projects)
fmt.Printf("PasswordHash: %s\n", user.PasswordHash) // PasswordHash 被成功读取
fmt.Printf("IsAdmin: %t\n", user.IsAdmin)
fmt.Println()
// 假设我们现在要将这个用户对象发送给客户端
// 我们只序列化其公共信息部分 (UserInfo)
var respBuffer bytes.Buffer
userBytes, err := json.Marshal(user.UserInfo) // 注意这里是 user.UserInfo
if err != nil {
log.Fatalf("序列化失败: %v", err)
}
json.Indent(&respBuffer, userBytes, "", " ")
fmt.Println("--- 序列化结果 (UserInfo 结构体) ---")
fmt.Println(respBuffer.String())
// 输出将不包含 "passwordHash" 字段
}运行上述代码,你会看到 PasswordHash 在反序列化后成功存储在 user 对象中,但在序列化为JSON响应时,它被正确地省略了。
通过采用结构体分离策略,我们可以在Go语言中优雅地实现JSON字段的单向处理,确保敏感数据在传输过程中的安全性,并使API接口更加精简和符合预期。
以上就是Go语言中实现JSON字段的单向序列化与反序列化:结构体分离策略的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号