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

本文探讨了在 Go 语言中使用 `encoding/json` 包处理 JSON 数据时,如何保留结构体中未定义的、动态的 JSON 字段。我们将介绍使用 `json.RawMessage` 类型以及自定义 `Unmarshaler` 和 `Marshaler` 接口的两种方法,以便在解码和编码 JSON 数据时,能够灵活地处理未知字段,从而避免完全使用 `map[string]interface{}` 带来的复杂性。
在 Go 语言中处理 JSON 数据时,我们经常会遇到需要将 JSON 数据解码到结构体中,进行一些操作,然后再将修改后的数据编码回 JSON 格式的需求。然而,JSON 数据中可能包含一些我们预先不知道或者不需要在结构体中定义的字段。如果我们简单地使用 json.Unmarshal 将 JSON 数据解码到结构体中,那么这些未定义的字段将会丢失。本文将介绍几种在 Go 中维护这些未解析 JSON 字段的方法,以便在重新编码时能够保留这些字段。
使用 json.RawMessage
json.RawMessage 类型允许我们将 JSON 数据的一部分表示为原始的 JSON 数据,而不进行具体的解码。这使得我们可以在结构体中定义一个 json.RawMessage 类型的字段,用于存储我们不关心的 JSON 部分。
例如,假设我们有以下 JSON 数据:
{
"name": "Joe Smith",
"age": 42,
"phone": "614-555-1212",
"debug": true,
"codeword": "wolf"
}我们只想处理 name、age 和 address 字段,而保留 debug 和 codeword 字段。我们可以定义如下结构体:
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age uint `json:"age"`
Address json.RawMessage `json:"address"` // Store unknown fields
}
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, Age: %d\n", p.Name, p.Age)
// Happy birthday
p.Age++
data, err := json.Marshal(p)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(string(data))
}在这个例子中,Address 字段是一个 json.RawMessage 类型,它会存储 JSON 数据中 address 字段的原始 JSON 数据。当我们使用 json.Marshal 重新编码结构体时,Address 字段中的原始 JSON 数据会被保留。
输出:
Pinokio
Pinokio是一款开源的AI浏览器,可以安装运行各种AI模型和应用
232
查看详情
Name: Joe Smith, Age: 42
{"name":"Joe Smith","age":43,"address":{"phone": "614-555-1212", "debug": true, "codeword": "wolf"}}注意事项:
- json.RawMessage 实际上是一个 []byte 类型,因此在使用它时需要注意类型转换。
- json.RawMessage 存储的是原始的 JSON 数据,因此在处理时需要小心,避免出现 JSON 格式错误。
- json.RawMessage 适用于存储嵌套的 JSON 对象或数组,对于简单的键值对,使用 map[string]interface{} 可能更方便。
使用自定义 Unmarshaler 和 Marshaler 接口
另一种方法是实现自定义的 Unmarshaler 和 Marshaler 接口。Unmarshaler 接口允许我们自定义 JSON 解码的行为,Marshaler 接口允许我们自定义 JSON 编码的行为。
通过实现这两个接口,我们可以在解码时将未知的字段存储到一个 map[string]interface{} 类型的字段中,然后在编码时将这些字段重新添加到 JSON 数据中。
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age uint `json:"age"`
Phone string `json:"phone"`
UnknownFields map[string]interface{} `json:"-"` // Ignore during standard marshaling
}
func (p *Person) UnmarshalJSON(data []byte) error {
// Define a type to *oid recursion
type Alias Person
aux := &Alias{}
if err := json.Unmarshal(data, &aux); err
!= nil {
return err
}
*p = Person(*aux)
// Unmarshal to a map to capture unknown fields
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
return err
}
// Remove known fields from the map
delete(m, "name")
delete(m, "age")
delete(m, "phone")
// Store unknown fields
p.UnknownFields = m
return nil
}
func (p Person) MarshalJSON() ([]byte, error) {
// Define a type to *oid recursion
type Alias Person
aux := &Alias{
Name: p.Name,
Age: p.Age,
Phone: p.Phone,
}
data, err := json.Marshal(aux)
if err != nil {
return nil, err
}
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
return nil, err
}
// Add unknown fields to the map
for k, v := range p.UnknownFields {
m[k] = v
}
return json.Marshal(m)
}
func main() {
jsonData := []byte(`{ "name": "Joe Smith", "age": 42, "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, Age: %d, Phone: %s\n", p.Name, p.Age, p.Phone)
// Happy birthday
p.Age++
data, err := json.Marshal(p)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(string(data))
}输出:
Name: Joe Smith, Age: 42, Phone: 614-555-1212
{"age":43,"codeword":"wolf","debug":true,"name":"Joe Smith","phone":"614-555-1212"}注意事项:
- 在实现 UnmarshalJSON 和 MarshalJSON 接口时,需要注意避免无限递归调用。
- UnknownFields 字段使用了 json:"-" 标签,这意味着在默认的编码过程中,该字段会被忽略。
- 这种方法需要编写更多的代码,但提供了更大的灵活性,可以自定义解码和编码的行为。
总结
本文介绍了两种在 Go 语言中维护未解析 JSON 字段的方法:使用 json.RawMessage 和使用自定义 Unmarshaler 和 Marshaler 接口。选择哪种方法取决于具体的需求和场景。如果只需要保留部分 JSON 数据,json.RawMessage 是一个简单有效的选择。如果需要更灵活地处理 JSON 数据,可以考虑实现自定义的 Unmarshaler 和 Marshaler 接口。无论选择哪种方法,都需要仔细考虑 JSON 数据的结构和字段,以便正确地处理未知字段。
以上就是在 Go 中维护未解析的 JSON 字段的最佳方法的详细内容,更多请关注其它相关文章!
# word
# 键值
# seo2.bdsmtv.cc
# 话剧营销推广
# seo免费优化软件排名
# 四川品质网站推广前景
# 天心区全网营销推广
# 花都网站建设团队
# 鞍山猫药网站建设
# 网站建设需要的硬件环境
# 网站优化四大因素是什么
# 房地产营销线下推广
# 时将
# 哪种
# 两种
# 我们可以
# 转换为
# 是一个
# 递归
# 文档
# 自定义
# 键值对
# ai
# app
# 编码
# go
# json
# js
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
cad怎么合并重叠的线段_cad清理重复重叠线条的操作方法
LINUX的I/O重定向是什么_深入理解LINUX中 >、>> 与 < 的区别
uc手机浏览器网页版入口 uc浏览器手机版便捷登录首页
怎么去除衣服上的口红印_生活小妙招教你用酒精轻松擦除
QQ邮箱官方登录入口_QQ邮箱网页版快捷使用平台
移动端XML文件怎么转换成Excel 手机和平板上的解决方案
J*a如何使用AtomicInteger控制计数_J*a无锁计数器性能分析
Win11怎么开启高性能模式_Windows 11电源计划优化设置
海量存储:机器视觉智能化的核心基石
4399体育竞技小游戏_4399小游戏赛事入口
Win10怎么设置静态IP地址 Win10手动配置IP地址步骤【指南】
在React函数组件中利用原生HTML5进行邮箱地址验证
Spring Boot内嵌服务器与J*a EE全栈特性:选择与部署策略
iCloud登录入口网页版 苹果iCloud官网登录
深入理解J*aScript中的B样条曲线与节点向量生成
一加 14R 快充无反应_一加 14R 充电优化
必由学官方平台入口 必由学在线课堂登录地址
QQ邮箱在线登录平台 QQ邮箱个人邮箱网页版入口
不会效仿卡普空!《铁拳》制作人澄清:不采取赛事付费|直播|
优化大型XML文件解析:基于Python流式处理的内存高效方案
windows10怎么关闭系统提示音_windows10彻底静音设置方法
Excel Power Pivot如何处理XML数据源 构建高级数据模型
漫蛙2正版漫画站 漫蛙2网页版快速访问入口
怎样使用“本地安全策略”提升Windows安全性_Secpol.msc配置指南【高手】
高德地图怎么看全景照片_高德地图全景照片浏览教程
Adobe PDF表单中利用J*aScript解析与格式化日期组件的教程
深入理解J*a链表中的IPosition接口与使用
漫画星球免费下拉式入口 漫画星球免费漫画在线阅读网站
谷歌google账号注册详细步骤 谷歌账号注册官方教程
利用5118提升短视频内容效果_5118短视频关键词优化方法
J*aScript map 迭代中检测空数组元素的有效方法
天猫2025双十一0点秒杀攻略 天猫爆款抢购时间
解决Python logging 中 datefmt 导致时间戳固定不变的问题
谷歌浏览器无痕模式怎么开 Chrome开启无痕浏览设置方法【教程】
c++如何使用chrono库处理时间_c++标准库时间与日期操作
J*a中实现Go语言select通道多路复用机制
Animex动漫社网入口地址 Animex动漫社网正版在线入口
C#中解析不规范的HTML为XML 常见的坑与解决办法
深入理解J*aScript Promise异步执行与微任务队列
PS5 Pro有点优势但不多! 《燕云十六声》PS5平台与PC性能画面对比
Golang切片为何属于引用类型_Golang slice底层结构与引用语义说明
机器学习中对数变换预测结果的反向还原
QQ邮箱网页版入口 QQ邮箱官方邮箱登录通道
12306选座系统怎么选连座_12306选座多人连坐操作方法
使用 Pandas 高效处理 .dat 文件:数据清洗与数值计算实战
Win11怎么开启省电模式_Win11电池节电模式自动开启
漫蛙MANWA漫画主页官方入口 漫蛙漫画最新在线阅读地址
今日头条怎么同步内容到抖音_今日头条内容同步到抖音教程
c++20的std::jthread是什么_c++可中断线程与RAII式管理
Win10文件资源管理器“此电脑”分组怎么关 Win10恢复经典视图【技巧】


2025-10-29
浏览次数:次
返回列表
!= nil {
return err
}
*p = Person(*aux)
// Unmarshal to a map to capture unknown fields
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
return err
}
// Remove known fields from the map
delete(m, "name")
delete(m, "age")
delete(m, "phone")
// Store unknown fields
p.UnknownFields = m
return nil
}
func (p Person) MarshalJSON() ([]byte, error) {
// Define a type to *oid recursion
type Alias Person
aux := &Alias{
Name: p.Name,
Age: p.Age,
Phone: p.Phone,
}
data, err := json.Marshal(aux)
if err != nil {
return nil, err
}
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
return nil, err
}
// Add unknown fields to the map
for k, v := range p.UnknownFields {
m[k] = v
}
return json.Marshal(m)
}
func main() {
jsonData := []byte(`{ "name": "Joe Smith", "age": 42, "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, Age: %d, Phone: %s\n", p.Name, p.Age, p.Phone)
// Happy birthday
p.Age++
data, err := json.Marshal(p)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(string(data))
}