新闻中心
在 Go 中维护未解析的 JSON 字段的最佳实践

本文介绍了在 Go 语言中使用 `encoding/json` 包处理 JSON 数据时,如何保留未解析的动态字段。针对需要在 Go 结构体中解码、操作后再编码回 JSON,但又不想丢失原始 JSON 中结构体未定义的字段的情况,提供了使用 `json.RawMessage` 类型和自定义 `Unmarshaler`/`Marshaler` 接口的解决方案,并简要提及了其他库(如 `mgo/bson`)中类似功能的实现。
在使用 Go 语言处理 JSON 数据时,经常会遇到这样的场景:我们需要将 JSON 数据解码到 Go 结构体中进行操作,然后再将修改后的结构体编码回 JSON。然而,原始 JSON 数据中可能包含一些我们事先未知的、或者暂时不需要处理的字段。如果直接使用 json.Unmarshal 将 JSON 数据解码到结构体中,这些未定义的字段就会被忽略,导致信息丢失。
那么,如何在 Go 中既能方便地使用结构体进行数据操作,又能保留原始 JSON 数据中的未解析字段呢?本文将介绍几种可行的方案。
使用 json.RawMessage 类型
json.RawMessage 是 encoding/json 包提供的一个类型,它本质上是 []byte 的别名。我们可以将结构体中的某个字段定义为 json.RawMessage 类型,这样在解码 JSON 数据时,该字段对应的 JSON 片段会被原封不动地存储为字节数组,而不会被进一步解析。
例如,假设我们有以下 JSON 数据:
{
"name": "Joe Smith",
"age": 42,
"phone": "614-555-1212",
"debug": true,
"codeword": "wolf"
}我们希望将其中的 name、age 和 address 字段解码到结构体中,而保留其他字段。可以定义如下结构体:
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age uint `json:"age"`
Address json.RawMessage `json:"address"` // 包含 "phone", "debug", "codeword" 等字段
}
func main() {
jsonData := []byte(`{ "name": "Joe Smith", "age": 42, "address": { "phone": "614-555-1212", "debug": true, "codeword": "wolf" } }`)
var p Person
err := json.Unmarshal(jsonData, &p)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Name: %s\n", p.Name)
fmt.Printf("Age: %d\n", p.Age)
fmt.Printf("Address (Raw): %s\n", string(p.Address)) // 输出原始 JSON 片段
// 修改 Age
p.Age++
// 重新编码为 JSON
newData, err := json.Marshal(p)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("New JSON: %s\n", string(newData))
}在这个例子中,Address 字段被定义为 json.RawMessage 类型。在解码 JSON 数据时,address 字段对应的 JSON 对象 { "phone": "614-555-1212", "debug": true, "codeword": "wolf" } 会被存储到 p.Address 中。当我们重新编码 Person 结构体为 JSON 时,address 字段的内容会被原封不动地放回 JSON 数据中。
注意事项:
- 使用 json.RawMessage 时,需要确保 JSON 数据中的对应字段是一个有效的 JSON 片段。
- 如果需要对 json.RawMessage 中存储的 JSON 数据进行进一步处理,需要手动进行解码。
自定义 Unmarshaler 和 Marshaler 接口
另一种方案是实现自定义的 Unmarshaler 和 Marshaler 接口。这种方案更加灵活,可以实现更复杂的逻辑,但同时也需要编写更多的代码。
Pinokio
Pinokio是一款开源的AI浏览器,可以安装运行各种AI模型和应用
232
查看详情
Unmarshaler 接口定义了 UnmarshalJSON 方法,该方法用于将 JSON 数据解码到对象中。Marshaler 接口定义了 MarshalJSON 方法,该方法用于将对象编码为 JSON 数据。
我们可以实现自定义的 UnmarshalJSON 方法,将 JSON 数据解码到一个 map[string]interface{} 中,然后将需要处理的字段提取到结构体中,并将剩余的字段存储到 map[string]interface{} 中。在实现 MarshalJSON 方法时,将结构体中的字段和 map[string]interface{} 中的字段合并,然后编码为 JSON 数据。
以下是一个示例:
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age uint `json:"age"`
Extra map[string]interface{} `json:"-"` // 忽略,用于存储未解析的字段
}
// UnmarshalJSON 自定义解码逻辑
func (p *Person) UnmarshalJSON(data []byte) error {
// 定义一个中间类型,避免无限递归
type Alias Person
aux := &struct {
*Alias
}{
Alias: (*Alias)(p),
}
// 先将 JSON 数据解码到 map[string]interface{} 中
var tmp map[string]interface{}
if err := json.Unmarshal(data, &tmp); err != nil {
return err
}
// 将需要处理的字段提取到结构体中
if name, ok := tmp["name"].(string); ok {
p.Name = name
delete(tmp, "name")
}
if age, ok := tmp["age"].(float64); ok { // 注意:JSON 中数字默认是 float64
p.Age = uint(age)
delete(tmp, "age")
}
// 将剩余的字段存储到 Extra 中
p.Extra = tmp
return nil
}
// MarshalJSON 自定义编码逻辑
func (p *Person) MarshalJSON() ([]byte, error) {
// 创建一个新的 map,包含结构体中的字段和 Extra 中的字段
tmp := make(map[string]interface{})
tmp["name"] = p.Name
tmp["age"] = p.Age
for k, v := range p.Extra {
tmp[k] = v
}
// 将 map 编码为 JSON 数据
return json.Marshal(tmp)
}
func main() {
jsonData := []byte(`{ "name": "Joe Smith", "age": 42, "phone": "614-555-1212", "debug": true, "codeword": "wolf" }`)
var p Person
err := json.Unmarshal(jsonData, &
amp;p)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Name: %s\n", p.Name)
fmt.Printf("Age: %d\n", p.Age)
fmt.Printf("Extra: %+v\n", p.Extra)
// 修改 Age
p.Age++
// 添加新的字段
p.Extra["new_field"] = "new_value"
// 重新编码为 JSON
newData, err := json.Marshal(p)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("New JSON: %s\n", string(newData))
}在这个例子中,Person 结构体包含一个 Extra 字段,类型为 map[string]interface{}。UnmarshalJSON 方法将 JSON 数据解码到 map[string]interface{} 中,并将 name 和 age 字段提取到结构体中,将剩余的字段存储到 Extra 中。MarshalJSON 方法将结构体中的字段和 Extra 中的字段合并,然后编码为 JSON 数据。
注意事项:
- 在实现 UnmarshalJSON 方法时,需要注意处理 JSON 数据中的类型转换。例如,JSON 中的数字默认是 float64 类型,需要将其转换为 uint 类型。
- 为了避免无限递归,可以在 UnmarshalJSON 方法中使用一个中间类型。
其他库
除了 encoding/json 包,还有一些其他的库也提供了类似的功能。例如,labix.org/v2/mgo/bson 库的 inline tag flag 可以将结构体中的字段内联到 BSON 文档中,从而实现保留未解析字段的功能。
总结
本文介绍了在 Go 语言中使用 encoding/json 包处理 JSON 数据时,如何保留未解析的动态字段。我们可以使用 json.RawMessage 类型或者自定义 Unmarshaler 和 Marshaler 接口来实现这个功能。选择哪种方案取决于具体的应用场景和需求。如果只需要简单地保留未解析的字段,可以使用 json.RawMessage 类型。如果需要实现更复杂的逻辑,可以自定义 Unmarshaler 和 Marshaler 接口。
以上就是在 Go 中维护未解析的 JSON 字段的最佳实践的详细内容,更多请关注其它相关文章!
# 原封不动
# 企业认证检测推广网站
# 肇庆服务网站优化软件
# 演讲网站建设文案
# 专业号薯条营销推广
# 推广类营销小程序
# 郴州网站建设的背景
# 英文网站花钱推广
# 推广网络营销服务热线
# 丽水关键词排名稳定提升
# 深圳公司网站建设设计
# 可以实现
# 可以使用
# 并将
# word
# 在这个
# 是一个
# 转换为
# 文档
# 递归
# 自定义
# ai
# 字节
# 编码
# go
# json
# js
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
Win11怎么关闭触摸屏_Windows 11禁用HID符合标准触摸屏
MAC如何将整个网页截长图_MAC使用Safari的导出为PDF或第三方工具
Golang指针如何与map组合使用_Golang map指针组合实践
深入理解Promise链:如何在catch后中断then的执行
为什么我的微信朋友圈看不到别人的更新_微信朋友圈更新显示异常解决方法
QQ邮箱登录首页官网地址2026 QQ邮箱官方网页入口
初次安装JDK时环境变量如何正确配置_J*A_HOME与PATH设置规则讲解
2026年发布! 美少女养成动作RPG《神剑少女战记》发布实机演示
mcjs网页版流畅运行 mcjs低配电脑畅玩入口
理解J*aScript Promise的微任务队列与执行顺序
c++中的const_cast和reinterpret_cast怎么用_c++四种类型转换
Golang如何使用bytes.Split分割字节切片_Golang bytes切片分割方法
深入理解Google Cloud Datastore查询:祖先路径与数据一致性
Golang如何实现容器化日志收集与分析_Golang容器日志收集分析方法
必由学网页版入口 必由学官方平台直接访问
CSS实现侧边栏导航项全宽圆角悬停背景效果
《刺客信条:影》PS5 Pro和Switch 2画面对比
台积电1.4nm工艺A14瞄准2028:10年来性能提升80%
sublime怎么进行远程开发编辑_配置rsub/rmate实现sublime编辑服务器文件
如何高效处理PHP中的Excel数据导入导出?PortPHP/Spreadsheet助你轻松搞定!
在J*a中如何开发在线活动报名与管理系统_活动报名管理项目实战解析
AI泡沫首次被“刺破”:GPU十年都无法存活!
sublime怎么覆盖插件的默认快捷键_sublime快捷键优先级与设置
CKEditor 5 自定义构建在React应用中渲染失败的调试与解决
动漫共和国防屏蔽稳定域名-动漫共和国官方正版直达通道
c++项目目录结构应该如何组织_c++工程化项目结构规范
J*a应用程序首次运行自动创建文件与目录的最佳实践
星露谷物语官网入口 星露谷物语游戏官网入口
Python自定义类排序:解决lambda键值访问TypeError的实践指南
如何在Promise链中优雅地中断后续then执行
58动漫网在线官方网 58动漫网正版动漫入口网址
vivo浏览器自带的下载器速度慢怎么办 vivo浏览器提升文件下载速度的技巧
AO3最新镜像入口 Archive of Our Own官方平台访问
AO3最新入口2025公告_AO3中文官网合集
iwriter统一登录平台 iwrite账号密码登录页面
怎么去除衣服上的口红印_生活小妙招教你用酒精轻松擦除
Python多版本共存与虚拟环境管理深度指南
处理嵌套交互式控件:前端可访问性指南
Archive of Our Own官网直达 AO3最新可用地址一览
虚幻5科幻题材ARPG大作遭取消!本是《奇异人生》厂商新作
mc.js游戏直达 mc.js网页免下载版本秒进地址
UE5.7引擎表现爆炸优化无敌!5090跑4K稳定60FPS
VS Code远程开发时如何处理文件权限问题
J*a递归快速排序中静态变量导致数据累积的陷阱与解决方案
Composer如何在生产环境安全地执行composer update
Yandex免登录官网入口_俄罗斯Yandex搜索引擎直达链接
c++20的std::jthread是什么_c++可中断线程与RAII式管理
windows10怎么查看硬盘序列号_windows10硬盘id查询命令
PDF怎么合并PDF并保持格式_PDF合并文件保持排版教程
Android Studio计算器C键功能异常排查与修复教程


2025-10-29
浏览次数:次
返回列表
amp;p)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Name: %s\n", p.Name)
fmt.Printf("Age: %d\n", p.Age)
fmt.Printf("Extra: %+v\n", p.Extra)
// 修改 Age
p.Age++
// 添加新的字段
p.Extra["new_field"] = "new_value"
// 重新编码为 JSON
newData, err := json.Marshal(p)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("New JSON: %s\n", string(newData))
}