讲师中心 微信公众号

工具推荐

视频效率加速
updream

一键生成高质量推广成片

AniShort
AniShort

AI短剧协同制作平台

Seko
Seko

AI短剧一键生成成片

剧云
剧云

中文短剧剧本制作平台

Go JSON:如何让结构体字段只被反序列化而不被序列化

大敏大大_5516

大敏大大_5516

发布时间:2025-10-28 14:38:43

|

505人浏览过

|

来源于php中文网

原创

Go JSON:如何让结构体字段只被反序列化而不被序列化

本文探讨在go语言中如何实现json结构体字段的选择性序列化与反序列化,即某个字段只在反序列化时读取,而在序列化时忽略。针对`json:"-"`标签无法满足此需求的问题,文章提出通过语义分离,将结构体拆分为不同用途的类型,并利用结构体嵌入实现这一目标,同时保持代码的清晰性和可维护性。

在Go语言的开发实践中,我们经常需要处理JSON数据的序列化(marshaling)和反序列化(unmarshaling)。有时,一个结构体字段可能在接收外部数据时是必需的(例如敏感信息如密码哈希),但在向外部输出数据时却需要被隐藏或忽略。Go标准库encoding/json提供了结构体标签(json:"fieldName"或json:"-")来控制这一行为。然而,json:"-"标签会将字段在序列化和反序列化两个方向上都完全忽略,这无法满足只读不写的特定需求。

理解 json:"-" 标签的局限性

json:"-"标签的作用是告诉encoding/json包,在进行JSON编解码时完全忽略带有此标签的字段。这意味着无论是在将JSON数据解析到结构体(反序列化)时,还是将结构体转换为JSON数据(序列化)时,该字段都不会被处理。

考虑以下User结构体:

type User struct {
    UserName     string   `json:"userName"`
    Projects     []string `json:"projects"`
    PasswordHash string   `json:"-"` // 此标签会使PasswordHash在读写时均被忽略
    IsAdmin      bool     `json:"isAdmin"`
}

如果使用此结构体进行反序列化:

import (
    "encoding/json"
    "fmt"
)

func main() {
    jsonContent := []byte(`{"userName":"alice","projects":["proj1"],"passwordHash":"some_secret_hash","isAdmin":true}`)
    var user User
    err := json.Unmarshal(jsonContent, &user)
    if err != nil {
        fmt.Println("Unmarshal error:", err)
        return
    }
    fmt.Printf("反序列化结果: %+v\n", user)
    // 输出: 反序列化结果: {UserName:alice Projects:[proj1] PasswordHash: IsAdmin:true}
    // PasswordHash 字段为空,因为它被 `json:"-"` 忽略了
}

可以看到,PasswordHash字段在反序列化时被忽略了,这与我们的“只读不写”目标不符。

解决方案:语义分离与结构体嵌入

当输入和输出的语义对象存在差异时,最好的做法是在代码中也将其分离。这意味着,如果一个结构体用于接收所有数据(包括敏感字段),而另一个结构体用于对外展示或存储非敏感数据,那么它们应该被定义为不同的Go类型。这种方法通过明确区分数据的用途,避免了单一结构体在不同场景下行为不一致的困境。

我们可以将原始结构体拆分为两个:一个包含所有字段(用于内部处理和反序列化),另一个只包含非敏感字段(用于对外展示和序列化)。通过结构体嵌入(embedding),可以优雅地实现这一目标。

重构后的结构体定义:

SEO Keyword Density
SEO Keyword Density

SEO关键词密度优化与页面审计技能。使用场景:①提供页面路径与关键词进行SEO优化;②提升页面关键词密度;③提升搜索引擎排名;④进行SEO审计(meta标签、OG、hreflang等)。触发词:关键词密度、SEO优化、关键词优化等。

下载
// UserInfo 结构体用于对外暴露的用户信息,不包含敏感数据
type UserInfo struct {
    UserName string   `json:"userName"`
    Projects []string `json:"projects"`
    IsAdmin  bool     `json:"isAdmin"`
}

// User 结构体用于内部处理,包含所有字段,包括敏感数据
type User struct {
    UserInfo // 嵌入UserInfo,使其字段可直接访问
    PasswordHash string `json:"passwordHash"` // 此处不再使用 "-" 标签
}

在这个设计中:

  • UserInfo代表了用户公开可见或非敏感的信息。
  • User代表了完整的内部用户数据模型,它通过嵌入UserInfo继承了其所有字段,并额外包含了敏感的PasswordHash字段。

适配序列化与反序列化逻辑

有了新的结构体定义,我们可以相应地调整JSON的序列化和反序列化逻辑。

反序列化(读取所有字段)

反序列化时,我们将JSON内容完整地解析到包含所有字段的User结构体中。encoding/json包会自动处理嵌入结构体的字段。

import (
    "encoding/json"
    "fmt"
)

func main() {
    jsonContent := []byte(`{"userName":"alice","projects":["proj1"],"passwordHash":"some_secret_hash","isAdmin":true}`)
    var user User
    err := json.Unmarshal(jsonContent, &user)
    if err != nil {
        fmt.Println("Unmarshal error:", err)
        return
    }
    fmt.Printf("反序列化结果: %+v\n", user)
    // 输出: 反序列化结果: {UserInfo:{UserName:alice Projects:[proj1] IsAdmin:true} PasswordHash:some_secret_hash}
    // PasswordHash 字段现在可以正确地被读取了。
}

序列化(只写入非敏感字段)

序列化时,我们只对User结构体中的UserInfo部分进行序列化,从而忽略敏感的PasswordHash字段。

import (
    "bytes"
    "encoding/json"
    "fmt"
)

func main() {
    // 假设我们有一个完整的User对象
    userToMarshal := User{
        UserInfo: UserInfo{
            UserName: "bob",
            Projects: []string{"proj2", "proj3"},
            IsAdmin:  false,
        },
        PasswordHash: "another_secret_hash_for_bob",
    }

    // 关键:只序列化 userToMarshal.UserInfo 部分
    userBytes, err := json.Marshal(userToMarshal.UserInfo)
    if err != nil {
        fmt.Println("Marshal error:", err)
        return
    }

    // 为了美观输出,进行缩进
    var respBuffer bytes.Buffer
    json.Indent(&respBuffer, userBytes, "", "   ")
    fmt.Println("序列化结果:")
    fmt.Println(respBuffer.String())
    /* 输出:
    序列化结果:
    {
       "userName": "bob",
       "projects": [
          "proj2",
          "proj3"
       ],
       "isAdmin": false
    }
    */
    // PasswordHash 字段被成功忽略,不会出现在序列化结果中。
}

通过这种方式,我们实现了PasswordHash字段在反序列化时被读取,但在序列化时被忽略的目标。

注意事项与最佳实践

  1. 清晰性与可维护性: 这种方法明确了每个结构体的职责,提高了代码的可读性和可维护性。User代表完整的内部数据模型,UserInfo代表对外暴露的视图模型,这种分离符合“关注点分离”的原则。
  2. 安全性: 确保敏感数据不会意外地通过序列化操作泄露出去,这在处理用户凭证、API密钥等信息时尤为重要。
  3. 替代方案:
    • 自定义 MarshalJSON 和 UnmarshalJSON 方法: 结构体可以实现 json.Marshaler 和 json.Unmarshaler 接口,通过自定义方法来完全控制序列化和反序列化过程。这提供了最大的灵活性,但通常代码量更大,复杂性更高,适用于更复杂的逻辑或特殊格式需求。对于本文所述的简单场景,分离结构体通常是更简洁且易于理解的选择。
    • 临时匿名结构体: 在序列化时,可以创建一个只包含所需字段的匿名结构体,然后对其进行序列化。这种方法适用于一次性或特定上下文的需求,例如:
      // ... userToMarshal 定义同上 ...
      tempStruct := struct {
          UserName string   `json:"userName"`
          Projects []string `json:"projects"`
          IsAdmin  bool     `json:"isAdmin"`
      }{
          UserName: userToMarshal.UserName,
          Projects: userToMarshal.Projects,
          IsAdmin:  userToMarshal.IsAdmin,
      }
      userBytes, _ := json.Marshal(tempStruct)

      这种方法在字段较少时可行,但当字段较多或需要频繁操作时,不如分离的具名结构体清晰和可维护。

总结

通过将结构体按照其在不同操作(输入/输出)中的语义进行分离,并利用Go语言的结构体嵌入特性,我们可以优雅地解决JSON字段只读不写的问题。这种模式不仅使得代码逻辑更加清晰,也有效提升了数据处理的安全性和灵活性。在实际开发中,应根据具体场景和复杂性,选择最合适的结构体设计和JSON处理策略。对于需要区分读写权限的字段,语义分离通常是比自定义编解码方法更直接、更易于管理的方案。

热门AI工具

更多
火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

花生AI
花生AI Hot

一款AI视频创作工具,主要用于B站推出的AI视频创作工具,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

超级麦吉

一款通过数字员工协作完成调研、数据分析与文档制作的AI办公平台。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

2335

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

3822

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

1276

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

4439

2025.09.10

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

5414

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

5591

2025.07.04

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3248

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

5449

2025.10.17

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

20

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WEB前端教程【HTML5+CSS3+JS】
WEB前端教程【HTML5+CSS3+JS】

共101课时 | 21.3万人学习

JS进阶与BootStrap学习
JS进阶与BootStrap学习

共39课时 | 4.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn