Go语言中实现JSON字段的单向序列化与反序列化:结构体分离策略

Go语言中实现JSON字段的单向序列化与反序列化:结构体分离策略

本文探讨了在go语言中如何实现json字段的单向处理,即允许字段从json反序列化(读取)但阻止其在序列化(写入)时出现。针对 `json:”-“` 标签无法满足此需求的问题,文章提出了一种有效的结构体分离策略。通过定义两个语义不同的结构体——一个用于内部完整数据表示,另一个用于外部公共数据传输——可以灵活控制字段的序列化行为,从而实现敏感信息的保护和api接口的精简。

在Go语言的Web服务开发中,我们经常需要处理结构体与JSON数据之间的转换。一个常见的需求是,某个结构体字段在从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接口定义不同的结构体。

具体来说,我们可以定义两个结构体:

UserInfo: 包含所有可以公开或需要序列化到客户端的字段。User: 包含所有内部字段,包括敏感字段,并通过嵌入 UserInfo 来继承其公共字段。

下面是修改后的结构体定义:

package mainimport (    "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: %sn", user.UserName)    fmt.Printf("Projects: %vn", user.Projects)    fmt.Printf("PasswordHash: %sn", user.PasswordHash) // PasswordHash 被成功读取    fmt.Printf("IsAdmin: %tn", 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 mainimport (    "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: %sn", user.UserName)    fmt.Printf("Projects: %vn", user.Projects)    fmt.Printf("PasswordHash: %sn", user.PasswordHash) // PasswordHash 被成功读取    fmt.Printf("IsAdmin: %tn", 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响应时,它被正确地省略了。

注意事项与总结

语义清晰:这种方法强制我们区分内部数据模型和外部API模型,这使得代码的意图更加清晰,也符合“关注点分离”的原则。灵活性:如果将来需要暴露 PasswordHash(例如在某个特殊的内部API中),只需序列化 User 结构体即可,而无需修改 UserInfo。维护性:当结构体字段增减时,只需要在对应的结构体中修改,对其他部分的影响较小。替代方案:虽然可以通过实现 json.Marshaler 和 json.Unmarshaler 接口来达到类似目的,但对于这种简单的单向需求,结构体分离通常更简洁、易读。自定义接口更适用于需要复杂逻辑处理(如类型转换、数据验证)的场景。

通过采用结构体分离策略,我们可以在Go语言中优雅地实现JSON字段的单向处理,确保敏感数据在传输过程中的安全性,并使API接口更加精简和符合预期。

以上就是Go语言中实现JSON字段的单向序列化与反序列化:结构体分离策略的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1419308.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
如何识别Go二进制文件编译时使用的Go版本
上一篇 2025年12月16日 13:03:19
Go 语言中 readUInt16BE 的等效实现与字节序处理
下一篇 2025年12月16日 13:03:34

相关推荐

  • 访问DeepSeek官方网站 deepseek在线版免费登录

    答案:DeepSeek在线版免费登录入口位于官网https://chat.deepseek.com/sign_in,用户可通过手机号验证码或微信授权登录,新用户免注册,登录后自动创建账户并同步多端数据,支持网页和APP使用。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 De…

    2026年9月21日
    000
  • Windows11提示“此电脑无法运行Windows 11”但已经安装了怎么办_Windows11提示无法运行系统修复方法

    首先检查并启用TPM 2.0与安全启动,进入UEFI设置开启相关选项;若硬件接近要求,可通过注册表新建AllowUpgradesWithUnsupportedTPMOrCPU并设值为1跳过检查;运行sfc /scannow修复系统文件;专业版用户还可通过组策略启用“移除此电脑不符合Windows 1…

    2026年9月21日
    100
  • 如何快速在大量文件中进行全局搜索和替换?

    修改文件内容用Word或Notepad++批量替换,改文件名则用星优或核烁等重命名工具,结合Everything快速定位,提升效率。 面对大量文件时,全局搜索和替换的关键是选对工具。手动一个一个处理效率太低,用对方法能省下大量时间。核心思路是:根据你要改的是“文件里的文字”还是“文件的名字”,选择不…

    2026年9月21日
    200
  • Linux系统信息查看命令整理

    答案:掌握Linux系统需从系统信息、资源使用、性能瓶颈、日志分析和用户权限五方面入手。uname、lscpu、free、df、ip、ss等命令用于查看系统软硬件状态;top、htop、vmstat、iostat、iftop等可诊断CPU、内存、磁盘、网络性能瓶颈;/var/log日志文件结合jou…

    2026年9月20日
    100
  • 灵绘AI如何生成艺术画_灵绘AI艺术画创作的完整流程

    首先启动灵绘AI并选择“艺术创作”模式,确保设备联网;接着在提示词框输入具体场景描述并添加风格关键词;然后调节细节等级至60%-80%、创意强度为7,并选择3:4或16:9比例;点击生成后预览结果,不满意可重新生成最多五次;对局部不满意区域使用“局部重绘”功能修改;最后导出时选择4K高清并保留图层信…

    2026年9月20日
    400
  • 怎么在VSCode里配置Go语言环境?

    安装Go并配置环境变量后,在VSCode中安装官方Go扩展,通过命令面板安装gopls、delve等必要工具,并设置保存时自动格式化与导入,即可实现代码补全、格式化和调试功能。 在 VSCode 中配置 Go 语言开发环境其实不复杂,只要安装好工具链并正确设置,就能获得代码补全、格式化、调试等完整功…

    2026年9月20日
    100
  • 事务(Transaction)处理与并发控制

    事务处理确保操作全部完成或不完成,并发控制防止事务互相干扰。事务处理核心是acid属性:1.原子性,2.一致性,3.隔离性,4.持久性;并发控制方法包括锁和mvcc,优化需考虑事务粒度、隔离级别、锁和mvcc的应用。 事务处理与并发控制是数据库管理系统中至关重要的两个概念,确保数据的一致性和完整性。…

    2026年9月20日
    100
  • Java从文本文件随机读取并打印指定行数内容

    本文旨在指导读者如何使用java程序从文本文件中高效地读取多组固定行数的内容(如诗歌),并随机选择其中一组进行打印。教程将详细介绍如何利用`files.readalllines`、`random`和`list.sublist`等核心api,实现文件的整体读取、随机索引的生成以及特定内容块的提取与输出…

    2026年9月20日
    100
  • edge浏览器”编写”功能(Copilot)怎么帮你润色文案_edge浏览器AI写作助手使用教程

    答案:通过Edge浏览器内置Copilot可实现文案润色。依次开启AI功能、登录账户,在文本框右键选择“使用Copilot改写”,可选正式、简短等语气风格,或通过侧边栏批量处理内容,提升写作效率。 如果您在撰写网页内容或文档时希望获得智能建议和语言优化,可以借助 Microsoft Edge 浏览器…

    2026年9月20日
    100
  • 如何为VSCode配置LaTeX环境,使其成为撰写学术论文的利器?

    首先安装LaTeX发行版和VSCode插件LaTeX Workshop,再配置XeLaTeX+BibTeX编译链,最后利用实时预览、反向搜索和代码片段等功能实现高效学术写作。 要在 VSCode 中配置 LaTeX 环境,打造高效的学术写作工具,关键在于正确安装和集成相关插件与编译工具。整个过程不复…

    2026年9月20日
    600
  • 小米Civi 3前置摄像头像素是多少 小米Civi 3自拍模式优化技巧

    小米Civi 3前置双3200万像素摄像头,配备78°f/2.0光圈主摄和100°超广角镜头,支持AF对焦与AI畸变矫正;通过切换镜头、开启4K拍摄、调节美颜、优化光线及启用EIS防抖,可显著提升自拍与Vlog画质表现。 小米Civi 3的前置摄像头配置在同级别中非常突出,自拍表现力强,配合一些使用…

    2026年9月20日
    100
  • 美篇如何添加背景音乐

    首先通过美篇内置音乐库或本地导入音频添加背景音乐,再调整自动播放与音量设置,确保阅读体验沉浸且不突兀。 如果您在编辑美篇文章时希望增加背景音乐以提升阅读体验,但不清楚具体操作步骤,可以按照以下方法进行设置。背景音乐能够为图文内容增添氛围,使读者获得更沉浸的浏览感受。 本文运行环境:iPad Air,…

    2026年9月20日
    100
  • PHP播放HLS视频流的方法_PHP播放HLS视频流方法

    答案:PHP通过权限控制和文件代理实现HLS流安全分发,前端使用HTML5视频标签和hls.js播放。具体描述:HLS将视频切为.ts片段并用.m3u8索引,PHP后端可校验用户权限、防止盗链,动态输出.m3u8或.ts内容;前端通过video标签加载stream.php?id=1,结合hls.js…

    2026年9月20日
    000
  • Java类的初始化顺序是怎样的 静态代码块和构造代码块先后

    Java类初始化顺序为:父类静态成员→子类静态成员→父类实例成员→父类构造函数→子类实例成员→子类构造函数,静态代码块仅加载时执行一次,构造代码块每次创建对象时执行,且均按书写顺序运行。 Java类的初始化顺序遵循一定的规则,理解这些顺序对掌握对象创建过程非常重要。当一个类被加载并创建实例时,各个代…

    2026年9月20日
    100
  • VSCode有哪些必备的插件?

    EditorConfig for VS Code统一代码风格,2. Prettier自动格式化多语言代码,3. ESLint检查JS/TS错误并集成Prettier,4. GitLens增强Git可视化,5. Path Intellisense补全文件路径,6. 括号高亮提升嵌套识别,7. Auto…

    2026年9月20日
    1000
  • Java从文本文件随机读取多行连续内容的教程

    本教程旨在指导java开发者如何高效地从文本文件中随机读取并打印指定数量(例如5行)的连续内容,尤其适用于处理结构化文本块(如诗歌)。我们将探讨如何避免仅读取文件开头固定行数的局限,通过将文件内容一次性加载到内存并结合随机数生成器来精确选取所需的文本块,从而实现真正的随机性与灵活性。 引言与问题分析…

    2026年9月20日
    200
  • 如何调整VSCode的设置以获得最佳性能?

    合理配置VSCode可显著提升性能。1. 禁用不必要扩展,减少后台资源占用;2. 在settings.json中设置files.watcherExclude和search.exclude以降低CPU负载;3. 启用editor.renderLineHighlight和largeFileOptimiz…

    2026年9月20日
    500
  • ChatGPT代码会出错吗_AI编程中5个常见错误及解决方法

    AI编程中常见错误包括语法不匹配、逻辑遗漏、API误用、安全漏洞和集成困难,需通过版本明确、测试验证、文档核对、安全扫描和上下文补充等方式解决,结合人工审查与测试才能确保代码质量。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ ChatGP…

    2026年9月20日
    100
  • 或为《刺客信条:起源》相关!巴耶克与艾雅动捕同框

    面容虽被遮挡,但魅力依旧啊!AI 伴学 + 轻薄便携,联想小新平板 12.1,开售啦! 帅是帅,就是比较废指头 FK阿凯 1.4万 0 《刺客信条:起源》的粉丝们近日惊喜地发现,曾为巴耶克与艾雅配音并进行面部捕捉的演员——阿布巴卡尔·萨利姆(Abubakar Salim)与艾莉克斯·威尔顿·里根(A…

    2026年9月20日
    000
  • RBAC(基于角色的权限控制)实现方案

    rbac重要,因为它通过角色管理权限,简化了权限管理,提高了系统安全和管理效率。实现rbac时:1.设计数据库结构,定义用户、角色、权限表及中间表;2.在代码中实现权限检查和角色、权限的动态管理;3.优化性能,防止权限泄露,管理角色膨胀。 在探讨RBAC(基于角色的权限控制)实现方案之前,让我们先来…

    2026年9月20日
    000

发表回复

登录后才能评论
关注微信