Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
Go语言中解析异构JSON数组的策略与实践_创想鸟

Go语言中解析异构JSON数组的策略与实践

Go语言中解析异构JSON数组的策略与实践

本文深入探讨了go语言中解析包含多种类型元素的json数组所面临的挑战,并提供了一种基于`json.rawmessage`的分步解析策略。通过将原始json数组首先解析为`json.rawmessage`切片,然后根据其结构特点进行二次解析,可以有效处理复杂的异构数据,确保数据准确映射到go结构体。

引言:Go语言中的JSON解析挑战

Go语言的encoding/json包提供了强大且高效的JSON序列化与反序列化能力。然而,在处理一些非标准或结构复杂的JSON数据时,开发者可能会遇到挑战。一个常见的场景是,当JSON数据是一个顶级数组,但其内部元素类型不一致时,直接使用Go结构体进行解析可能会导致错误。

问题分析:异构JSON数组的特殊性

考虑以下JSON结构:

[    {        "page": 1,        "pages": 6,        "per_page": "50",        "total": 256    },    [        {            "id": "ABW",            "iso2Code": "AW"        }    ]]

这个JSON是一个数组,但其第一个元素是一个包含分页信息的对象,第二个元素则是一个包含国家列表的数组。如果尝试将其直接解析到一个单一的Go结构体切片(例如 []Data),Go的JSON解析器会抛出 json: cannot unmarshal array into Go value of type main.Data 错误。这是因为encoding/json包在尝试将整个JSON数组的第一个元素(一个对象)解析到Data类型时成功,但当遇到第二个元素(一个数组)时,发现它与Data类型不匹配,从而导致解析失败。

为了成功解析这种异构数组,我们需要一种更灵活的策略,能够先将不同类型的元素作为原始JSON数据保留下来,再进行后续的针对性解析。

立即学习“go语言免费学习笔记(深入)”;

解决方案:利用json.RawMessage实现分步解析

json.RawMessage是encoding/json包提供的一个类型,它本质上是[]byte的别名,用于表示一个原始的JSON值。它的关键作用在于,当JSON解析器遇到json.RawMessage类型的字段时,它不会尝试解析其内部结构,而是直接将其原始字节内容存储起来。这为我们处理异构数组提供了完美的解决方案。

1. 定义数据结构

首先,我们需要为JSON中的不同部分定义对应的Go结构体。

package mainimport (    "encoding/json"    "fmt"    "log")// Data 结构体用于解析分页信息对象type Data struct {    Page    int    `json:"page"`    Pages   int    `json:"pages"`    PerPage string `json:"per_page"` // JSON中为字符串,Go中也定义为string    Total   int    `json:"total"`}// Country 结构体用于解析国家信息对象type Country struct {    Id       string `json:"id"`    Iso2Code string `json:"iso2Code"`}// DataCountry 是一个组合结构,用于存储解析后的一对数据和国家列表type DataCountry struct {    Data        Data      `json:"data"`    CountryList []Country `json:"country_list"`}

注意:PerPage字段在JSON中是一个字符串(”50″),所以在Go结构体中也应定义为string类型,并使用json:”per_page”标签进行映射。

2. 步骤一:初步解析为原始消息切片

我们将整个顶级JSON数组解析为一个[]json.RawMessage切片。这样,JSON解析器会将每个异构元素(无论是对象还是数组)都当作一个独立的原始JSON消息存储。

func main() {    body := []byte(`[    {        "page": 1,        "pages": 6,        "per_page": "50",        "total": 256    },    [        {            "id": "ABW",            "iso2Code": "AW"        }    ]]`)    // 使用 []json.RawMessage 来初步解析顶层数组    // raw 将包含两个元素:第一个是Data对象的原始JSON,第二个是Country列表的原始JSON    var raw []json.RawMessage    if err := json.Unmarshal(body, &raw); err != nil {        log.Fatalf("初步解析错误: %v", err)    }    // ... 后续处理}

此时,raw切片将包含两个json.RawMessage元素:

raw[0]:{“page”: 1, “pages”: 6, “per_page”: “50”, “total”: 256} 的字节表示。raw[1]:[{“id”: “ABW”, “iso2Code”: “AW”}] 的字节表示。

3. 步骤二:迭代并二次解析

由于我们知道JSON的结构是交替出现的数据对象和国家列表数组,我们可以遍历raw切片,每两个元素为一组进行处理。

func main() {    // ... (body 和 raw 的定义与初步解析)    sdc := make([]DataCountry, 0) // 用于存储最终解析结果的切片    // 每次迭代处理两个元素:一个Data对象和一个Country列表    for i := 0; i < len(raw); i += 2 {        dc := DataCountry{} // 创建一个DataCountry实例来存储当前对的数据        // 解析Data对象        data := Data{}        if err := json.Unmarshal(raw[i], &data); err != nil {            fmt.Printf("解析Data对象错误 (索引 %d): %vn", i, err)            // 根据实际需求处理错误,例如跳过或记录        } else {            dc.Data = data        }        // 解析Country列表        var countries []Country        // 确保 i+1 不越界,处理可能不完整的最后一对数据        if i+1 < len(raw) {            if err := json.Unmarshal(raw[i+1], &countries); err != nil {                fmt.Printf("解析Country列表错误 (索引 %d): %vn", i+1, err)            } else {                dc.CountryList = countries            }        } else {            fmt.Printf("警告: JSON数据可能不完整,缺少Country列表 (索引 %d)n", i+1)        }        sdc = append(sdc, dc) // 将解析完成的DataCountry添加到结果切片    }    fmt.Printf("解析结果: %+vn", sdc)}

完整示例代码

将以上所有部分整合,构成完整的Go程序:

package mainimport (    "encoding/json"    "fmt"    "log")// Data 结构体用于解析分页信息对象type Data struct {    Page    int    `json:"page"`    Pages   int    `json:"pages"`    PerPage string `json:"per_page"` // JSON中为字符串,Go中也定义为string    Total   int    `json:"total"`}// Country 结构体用于解析国家信息对象type Country struct {    Id       string `json:"id"`    Iso2Code string `json:"iso2Code"`}// DataCountry 是一个组合结构,用于存储解析后的一对数据和国家列表type DataCountry struct {    Data        Data      `json:"data"`    CountryList []Country `json:"country_list"`}func main() {    body := []byte(`[    {        "page": 1,        "pages": 6,        "per_page": "50",        "total": 256    },    [        {            "id": "ABW",            "iso2Code": "AW"        }    ]]`)    // 步骤一:使用 []json.RawMessage 来初步解析顶层数组    // raw 将包含两个元素:第一个是Data对象的原始JSON,第二个是Country列表的原始JSON    var raw []json.RawMessage    if err := json.Unmarshal(body, &raw); err != nil {        log.Fatalf("初步解析错误: %v", err)    }    sdc := make([]DataCountry, 0) // 用于存储最终解析结果的切片    // 步骤二:迭代并二次解析    // 每次迭代处理两个元素:一个Data对象和一个Country列表    for i := 0; i < len(raw); i += 2 {        dc := DataCountry{} // 创建一个DataCountry实例来存储当前对的数据        // 解析Data对象        data := Data{}        if err := json.Unmarshal(raw[i], &data); err != nil {            fmt.Printf("解析Data对象错误 (索引 %d): %vn", i, err)            // 根据实际需求处理错误,例如跳过或记录        } else {            dc.Data = data        }        // 解析Country列表        var countries []Country        // 确保 i+1 不越界,处理可能不完整的最后一对数据        if i+1 < len(raw) {            if err := json.Unmarshal(raw[i+1], &countries); err != nil {                fmt.Printf("解析Country列表错误 (索引 %d): %vn", i+1, err)            } else {                dc.CountryList = countries            }        } else {            fmt.Printf("警告: JSON数据可能不完整,缺少Country列表 (索引 %d)n", i+1)        }        sdc = append(sdc, dc) // 将解析完成的DataCountry添加到结果切片    }    fmt.Printf("最终解析结果: %+vn", sdc)    /*    输出示例:    最终解析结果: [{Data:{Page:1 Pages:6 PerPage:50 Total:256} CountryList:[{Id:ABW Iso2Code:AW}]}]    */}

注意事项与最佳实践

何时选择json.RawMessage:

当JSON结构不规则,顶级数组包含多种不同类型的元素时。当JSON数据包含某些你暂时不需要解析,或者需要根据条件延迟解析的复杂嵌套结构时。当JSON数据中某些字段的类型不确定,或者可能在不同情况下变化时。

健壮性考虑:

错误处理:在每次json.Unmarshal调用时都应进行错误检查。在处理循环中的错误时,需要决定是跳过当前元素、记录错误并继续,还是立即终止解析。数据完整性:在上述示例中,我们假设raw切片的长度是偶数。如果JSON数据不完整,例如只包含Data对象而没有对应的Country列表,i+1

字段标签 (json:”fieldName”) 的使用:

确保Go结构体字段名与JSON键名之间的映射正确。如果Go字段名与JSON键名不一致,需要使用json:”json_key_name”标签进行指定。对于JSON中为字符串但Go中希望解析为数字的字段,可以使用 json:”key_name,string” 标签来指示解析器尝试从字符串中解析数字。但在本例中,PerPage本身就是字符串,所以直接json:”per_page”即可。

总结

通过巧妙地利用json.RawMessage,Go语言开发者可以有效地处理那些直接解析会遇到困难的异构JSON数组。这种分步解析的策略提供了一种强大且灵活的方法,使得我们能够精确控制JSON数据的解析过程,确保复杂数据结构能够准确无误地映射到Go程序中的相应类型。在面对非标准或高度动态的JSON数据时,json.RawMessage无疑是解决这类问题的利器。

以上就是Go语言中解析异构JSON数组的策略与实践的详细内容,更多请关注创想鸟其它相关文章!

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

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Go TCP客户端即时发送数据:SetNoDelay的实践与服务器端诊断
上一篇 2025年12月16日 09:28:17
Golang异步HTTP服务器中的共享通信与状态管理
下一篇 2025年12月16日 09:28:37

相关推荐

  • 苹果手机忘记密码

    确认你的Apple ID与密码情况 首先,确认你是否真的忘记了密码。有时候可能是输错了Apple ID或密码,建议仔细核对。回想一下是否曾修改过密码,并检查与该账户绑定的邮箱、安全提示问题或双重认证设备,这些都有助于找回账户访问权限。 如何重设密码 若确认密码遗失,最有效的解决方式是进行密码重置。请…

    2026年9月22日
    000
  • Laravel 8 登录后重定向到仪表盘:完整教程

    本教程详细阐述了在 Laravel 8 中实现用户登录后重定向到仪表盘的多种方法。我们将探讨 Laravel 默认的重定向机制、如何正确配置仪表盘路由及其中间件,并提供通过自定义 LoginController 实现精确重定向的示例代码。通过本文,您将全面掌握 Laravel 认证后的重定向流程,并…

    2026年9月22日
    400
  • Ubuntu VMware Tools安装详细过程(非常靠谱)「建议收藏」

    Ubuntu VMware Tools安装详细过程(非常靠谱)「建议收藏」Ubuntu VMware Tools安装详细过程(非常靠谱)「建议收藏」Ubuntu VMware Tools安装详细过程(非常靠谱)「建议收藏」Ubuntu VMware Tools安装详细过程(非常靠谱)「建议收藏」

    大家好,很高兴再次与大家见面,我是你们的朋友全栈君。 说明:这篇博客是博主亲自编写的,内容独特,辛苦付出,请大家尊重原创,感谢支持! 一.前言VMware Ubuntu安装的详细指南:https://www.php.cn/link/35e7132c1742eaa9dacfedd5607b5f94。 …

    2026年9月22日 • 用户投稿
    800
  • VSCode如何集成Jai游戏开发环境 VSCode配置高性能游戏编程工作流

    配置#%#$#%@%@%$#%$#%#%#$%@_e2fc++805085e25c9761616c00e065bfe8集成jai游戏开发环境的核心在于正确设置编译器与调试器并利用扩展提升效率,1. 配置settings.json指定jai.compilerpath、builddirectory、in…

    2026年9月22日
    400
  • 为什么需要定期更新主板的BIOS,更新过程中断电会导致什么严重后果?

    定期更新BIOS可提升系统稳定性、硬件兼容性、安全性和性能。支持新CPU和内存需更新BIOS;修复启动异常、USB识别等问题;修补Spectre等安全漏洞;优化电源管理与超频能力。但更新中断可能导致BIOS损坏、主板无法开机,需专业修复,因此操作时须确保稳定供电并遵循厂商指引。 定期更新主板的BIO…

    2026年9月22日
    000
  • mac怎么使用iMovie剪辑视频_mac使用iMovie剪辑视频教程

    首先打开iMovie并导入视频素材,然后将视频拖入时间线进行裁剪与分割,接着为片段间添加转场效果,再插入背景音乐并调节音量,最后设置参数导出视频。 如果您想在Mac上对视频进行剪辑和编辑,但不知道如何使用系统自带的iMovie应用完成操作,可以按照以下步骤进行。iMovie提供了直观的界面和基础剪辑…

    2026年9月22日
    500
  • 淘宝签到红包如何兑换

    淘宝签到红包是平台为用户准备的一项实用福利,帮助你在日常购物中获得更多实惠。那么,如何顺利兑换这些签到红包呢?接下来为你一步步解析。 一、参与签到领取红包 打开淘宝App后,通常在首页就能看到醒目的“签到”入口,点击即可进入签到页面。每日坚持打卡,系统便会发放对应的签到红包。红包金额会根据连续签到的…

    2026年9月22日
    000
  • TensorFlow的AI混合工具怎么操作?构建机器学习模型的详细步骤

    TensorFlow的混合编程核心在于结合Keras的高级抽象与TensorFlow底层API的灵活性,实现高效模型开发。首先使用tf.data构建高性能数据管道,通过map、batch、shuffle和prefetch等操作优化数据预处理;接着利用Keras快速搭建模型结构,同时通过继承tf.ke…

    2026年9月21日
    300
  • Intel前CEO:公司过去15年连锁犯错、18A是重要里程碑

    10月14日,曾担任intel首席执行官的帕特·基辛格(pat gelsinger)在近期一次采访中分享了他对自身在intel职业生涯的反思,并就当下ai产业的发展态势表达了个人见解。 他坦言,Intel“在过去十五年间接连做出多项错误的战略选择”, 这使得公司进入了漫长的重建期,同时也失去了曾经在…

    2026年9月21日
    600
  • VSCode如何自定义文件图标 VSCode资源管理器视觉优化的技巧

    自定义vscode文件图标需安装图标主题扩展,如material icon theme;2. 通过扩展市场安装后,在文件图标主题设置中启用;3. 选择主题时应考虑视觉风格、图标覆盖率、辨识度和更新频率;4. 可结合文件嵌套、隐藏文件夹、缩进指南等设置优化资源管理器视觉体验;5. 自定义图标对性能影响…

    2026年9月21日
    000
  • 手机淘宝怎么加热区?淘宝怎么添加热区

    需商家账号在淘宝商家中心或旺铺PC端设置热区,普通买家无权限。①手机端:登录商家中心→店铺管理→详情页装修→选图添加热点→设链接保存;②PC端:登录旺铺官网→店铺装修→用图片热区工具划区域→设跳转链接→发布;③确认账号为已开店的商家主/子账号,未开通需申请店铺并订购旺铺服务。 如果您在使用手机淘宝时…

    2026年9月21日
    000
  • 如何使用Scikit-learn训练AI大模型?传统机器学习与深度结合

    如何使用Scikit-learn训练AI大模型?传统机器学习与深度结合如何使用Scikit-learn训练AI大模型?传统机器学习与深度结合如何使用Scikit-learn训练AI大模型?传统机器学习与深度结合如何使用Scikit-learn训练AI大模型?传统机器学习与深度结合

    Scikit-learn在大型模型预处理中的核心作用是提供数据清洗、特征缩放、编码和降维等工具,确保输入数据高质量且规范化,为深度学习模型奠定坚实基础。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ 说实话,如果你的目标是纯粹地“训练AI大…

    2026年9月21日 • 用户投稿
    700
  • Online Config VS Code

    Online Config VS CodeOnline Config VS CodeOnline Config VS CodeOnline Config VS Code

    run vs view Install Code Server Update Code Server Database:It is recommended to create a Docker container for the database. Code Language: JavaScript…

    2026年9月21日 • 用户投稿
    000
  • MediBangPaint中AI生成图片如何导出?快速保存图像的详细方法

    答案:导出AI生成图片应优先选择PNG格式以保留细节和色彩。通过“文件”菜单中的“导出(单层)”功能,可将图像保存为PNG或JPG等格式,其中PNG为无损压缩,适合高质量输出;若需透明背景或后续编辑,更应选用PNG。清晰度不足常因原始分辨率低、过度放大或JPG压缩过度所致,建议导出时设置高质量(80…

    2026年9月21日
    300
  • 百度搜索app如何启用搜索关键词过滤_百度搜索app关键词过滤的设置技巧

    可通过减号语法排除关键词,如“健身教程 -广告”;用双引号实现精确匹配,如”瑜伽初学者教程”;结合site:指令限定范围,如Python入门教程 site:zhihu.com,提升搜索精准度。 如果您在使用百度搜索App时,希望减少不相关或不想要的搜索结果,可以通过设置关键词…

    2026年9月21日
    800
  • Bun 1.3 正式发布

    2025年10月10日,高性能 javascript 运行时 bun 发布了 1.3 版本。这是 bun 项目迄今为止最重大的版本更新,标志着 bun 从单纯的运行时工具演变为一个功能完备的全栈 javascript 开发平台。 从运行时到全栈平台的跨越 Bun 1.3 的核心突破在于将前端开发能力…

    2026年9月21日
    100
  • Java中多态的基本实现方法

    多态允许同一接口调用不同实现,通过继承与方法重写实现。1. 子类重写父类方法,如Animal的makeSound被Dog和Cat重写;2. 父类引用指向子类对象,运行时动态绑定,如Animal myPet = new Dog()调用Woof;3. 方法参数使用父类类型,提升代码复用,如playWit…

    2026年9月21日
    100
  • 更偏向移动端?Steam新版商店页引国外玩家批评

    今日,v社正式上线全新版本的steam商店界面,标志着此前长期测试的新设计终于全面启用。新版首页在视觉上更加开阔、简洁,将原先位于左侧的游戏分类菜单与顶部的蓝色导航栏整合为统一的顶部导航条,支持用户直接浏览竞速、潜行等具体游戏类型,并结合用户偏好实现个性化内容推荐。整体布局更贴近移动端操作逻辑,页面…

    2026年9月21日
    000
  • safari浏览器如何设置链接在新窗口而不是新标签页打开_safari浏览器链接新窗口打开设置

    通过快捷键或第三方扩展可实现Safari中链接在新窗口打开:1. 按住Command键点击链接可临时在新窗口打开;2. 使用AppleScript脚本通过“自动操作”创建快速操作以新建Safari窗口;3. 网站自身代码如window.open()会强制新窗口打开;4. 安装可信扩展如“Link i…

    2026年9月21日
    100
  • Hibernate Search嵌入式对象索引策略与常见问题解决

    本文探讨了在使用Hibernate Search对关联或嵌入式对象进行索引时遇到的常见问题,特别是@IndexedEmbedded与includePaths属性的结合使用。通过分析HSEARCH000216错误,揭示了嵌入式对象属性需要显式@Field注解才能被主实体索引的机制,并提供了具体的代码示…

    2026年9月21日
    200

发表回复

登录后才能评论
关注微信