Go解析不定JSON数据的方法详解

 更新时间:2024年03月10日 08:21:22   作者:安妮的心动录  
在开发中常常会碰到很多JSON类型的数据进行交互,而其中有很多JSON数据你是不能确定它的字段和结构的,而Go语言是一门静态强类型的语言,在进行JSON解析的时候必须要确定字段的类型,本文给大家介绍了Go如何解析不定JSON数据,需要的朋友可以参考下

前言

在开发中常常会碰到很多JSON类型的数据进行交互,而其中有很多JSON数据你是不能确定它的字段和结构的,而Go语言是一门静态强类型的语言,在进行JSON解析的时候必须要确定字段的类型,定义出对应的结构体,然后再进行Unmarshal,那这二者之间的冲突我们该如何解决呢?

什么是JSON

  • json是JavaScript Object Notation(JavaScript对象表示法)
  • json是轻量级的文本数据交换格式
  • json独立于语言
  • json具有自我描述性,更容易理解
  • json使用js语法来描述数据对象,但是json仍然独立于语言和平台,json解析器和json库支持许多不同的编程语言

json是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成,之所以json这么流行,是因为json的结构和多级结构体(对象)刚好能对应上,并且本身也十分易读。而前后端交互的时候后端通常会返回给前端一个多级的结构体,于是json慢慢开始流行了,且json是跨语言和跨平台的,自身也足够轻量级。

json的几种标准格式

一个标准的json数据
//每个key对应的是一个value
{
“k1": 1,
"k2": 2 //注意结尾的这个不能有逗号
}



json字符串
{
"k1": "1",
"k2": "2"
}


json数组
{
“k1”: [1,2],
“k2”: [3,4]
}


json对象
{
“k1”: {“1”: “haihai”},
“k2”: {“2”:”haihahai”}
}


json对象数组
{
“k1”: [
{“k11”: “hellohello”},
{“k12”: “badbad”}
]
}



json数组对象
{
“k2”: {
	“hello”: [1,2,3]
	}
}

所有的JSON数据都是由上述几种JSON数据组合而成

如何在Go中解析不确定的JSON数据

通过看文档的方式去确定对应的JSON数据,然后构造对应的结构体

这是最靠谱的方式,最合理也是效率最高的方式。

// 请求其他服务   
jsonStr := xxx

var data interface{}

err := json.Unmarshal([]byte(jsonStr),&data)

fmt.Println(data)

比如可以先拿一个interface{}类型来接住JSON数据,然后看这个interface{}的值,来确定这个JSON数据哪些字段是string 哪些是object 哪些是int float等等
当然这也不是完全适用的,比如下面这种情况,有一个字段如下
type : []
能看出来type是一个切片类型的值,但是具体的类型你并不知道,可能是[]int 也有可能是[]string []float等等

map[string] interface{}

这个类型是map键值对,值可以是任意类型,因为在go中任意类型都实现了空接口interface{},而json数据也是key value的键值对,所以map[string] interface{}天然支持解析json类型数据

jsonStr := xxx
var data map[string]interface{} 
err := json.Unmarshal([]byte(jsonStr),&data)

// 你想取的字段
fieldValue := data["field"]

// 类型断言
if value,ok := data["field"].(float64);ok {

} else if vluae,ok := data["field"].(int64); ok {

}

理论上所有的合法的JSON数据都可以被反序列化到map[string]interface{}中
但是实际应用中 可能会出现一些无法被map[string]interface{}解析的JSON数据
  • JSON 数据中包含了多层嵌套的数据结构。在这种情况下,如果没有使用递归或者其他方式对嵌套数据进行处理,可能会导致反序列化失败。
  • JSON 数据中包含了数组类型,但是数组元素类型不一致或者无法转换成相应的类型。在这种情况下,可能需要手动处理数组元素或者使用其他数据类型来保存数组数据。
  • JSON 数据中包含了自定义数据类型或者复杂的数据结构,无法使用 map[string]interface{} 类型来反序列化。在这种情况下,需要定义相应的结构体或者使用其他适合的数据类型来反序列化。

第三方库

除了encoding/json之外,还有很多第三方库可以用来解析不确定的JSON数据,例如gjson和jsonparser,这些库通常提供了更加灵活和高效的JSON解析方式,可以根据具体的需求选择合适的库来使用

json.RawMessage与json.Number

  • json.RawMessage 是一个非常高效的数据类型,因为她不需要进行任何解析和类型转换,直接保存了未经处理的原始JSON数据,在反序列化的时候只需要将json.RawMessage转化为对应的数据类型即可,无需重新解析JSON数据
  • json.Number 表示JSON中的数字类型,可以用来保存任意精度的数字。这个数字可以特别大,可能会无法用Go中的整数或者浮点数来表示
package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    jsonData := []byte(`{
        "id": 12345,
        "name": "John Doe",
        "age": 30,
        "score": 95.5,
        "is_student": true,
        "tags": ["tag1", "tag2", "tag3"],
        "extra": {
            "field1": "value1",
            "field2": 123
        }
    }`)

    var m map[string]json.RawMessage
    err := json.Unmarshal(jsonData, &m)
    if err != nil {
        panic(err)
    }

    var id int
    err = json.Unmarshal(m["id"], &id)
    if err != nil {
        panic(err)
    }
    fmt.Printf("id: %d\n", id)

    var name string
    err = json.Unmarshal(m["name"], &name)
    if err != nil {
        panic(err)
    }
    fmt.Printf("name: %s\n", name)

    var age int
    err = json.Unmarshal(m["age"], &age)
    if err != nil {
        panic(err)
    }
    fmt.Printf("age: %d\n", age)

    var score float64
    err = json.Unmarshal(m["score"], &score)
    if err != nil {
        panic(err)
    }
    fmt.Printf("score: %f\n", score)

    var isStudent bool
    err = json.Unmarshal(m["is_student"], &isStudent)
    if err != nil {
        panic(err)
    }
    fmt.Printf("is_student: %v\n", isStudent)

    var tags []string
    err = json.Unmarshal(m["tags"], &tags)
    if err != nil {
        panic(err)
    }
    fmt.Printf("tags: %v\n", tags)

    var extra map[string]json.RawMessage
    err = json.Unmarshal(m["extra"], &extra)
    if err != nil {
        panic(err)
    }
    var field1 string
    err = json.Unmarshal(extra["field1"], &field1)
    if err != nil {
        panic(err)
    }
    fmt.Printf("extra.field1: %s\n", field1)

    var field2 int
    err = json.Unmarshal(extra["field2"], &field2)
    if err != nil {
        panic(err)
    }
    fmt.Printf("extra.field2: %d\n", field2)
}

// 不确定的类型
data := make(map[string]interface{})
if err := json.Unmarshal(rawData, &data); err != nil {
    log.Fatal(err)
}

if value, ok := data["age"].(float64); ok {
    // 处理年龄为浮点数的情况
} else if value, ok := data["age"].(int); ok {
    // 处理年龄为整数的情况
} else {
    // 处理年龄为其他类型或不存在的情况
}

需要注意的是:类型断言的底层为反射,因为在运行时需要判断一个接口值的具体类型,而这个类型是在编译时无法确定的,需要在运行时动态地获取。效率比正常的代码低一到两个数量级,而且需要消耗额外的时间和内存。

最后

以上就是Go解析不定JSON数据的方法详解的详细内容,更多关于Go解析不定JSON数据的资料请关注脚本之家其它相关文章!

相关文章

  • Go语言通过http抓取网页的方法

    Go语言通过http抓取网页的方法

    这篇文章主要介绍了Go语言通过http抓取网页的方法,实例分析了Go语言通过http操作页面的技巧,需要的朋友可以参考下
    2015-03-03
  • golang包循环引用的几种解决方案总结

    golang包循环引用的几种解决方案总结

    golang有包循环引用问题,用过的应该都知道,下面这篇文章主要给大家介绍了关于golang包循环引用的几种解决方案,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • Golang 探索对Goroutine的控制方法(详解)

    Golang 探索对Goroutine的控制方法(详解)

    下面小编就为大家分享一篇Golang 探索对Goroutine的控制方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-12-12
  • Golang实现异步上传文件支持进度条查询的方法

    Golang实现异步上传文件支持进度条查询的方法

    这篇文章主要介绍了Golang实现异步上传文件支持进度条查询的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • 深入理解Golang Channel 的底层结构

    深入理解Golang Channel 的底层结构

    这篇文章主要介绍了深入理解Golang Channel 的底层结构,Go 语言的 channel 底层是什么数据结构?下面我们就一起来深入解析一下 channel,需要的朋友可以参考下
    2022-01-01
  • Go语言实现Fibonacci数列的方法

    Go语言实现Fibonacci数列的方法

    这篇文章主要介绍了Go语言实现Fibonacci数列的方法,实例分析了使用递归和不使用递归两种技巧,并对算法的效率进行了对比,需要的朋友可以参考下
    2015-02-02
  • golang基础之waitgroup用法以及使用要点

    golang基础之waitgroup用法以及使用要点

    WaitGroup是Golang并发的两种方式之一,一个是Channel,另一个是WaitGroup,下面这篇文章主要给大家介绍了关于golang基础之waitgroup用法以及使用要点的相关资料,需要的朋友可以参考下
    2023-01-01
  • Golang IOT中的数据序列化与解析过程

    Golang IOT中的数据序列化与解析过程

    这篇文章主要介绍了Golang IOT中的数据序列化与解析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • Golang如何使用go.mod配置加载本地模块

    Golang如何使用go.mod配置加载本地模块

    这篇文章主要介绍了Golang如何使用go.mod配置加载本地模块问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • Golang 实现 Redis系列(六)如何实现 pipeline 模式的 redis 客户端

    Golang 实现 Redis系列(六)如何实现 pipeline 模式的 redis 客户端

    pipeline 模式的 redis 客户端需要有两个后台协程负责 tcp 通信,调用方通过 channel 向后台协程发送指令,并阻塞等待直到收到响应,本文是使用 golang 实现 redis 系列的第六篇, 将介绍如何实现一个 Pipeline 模式的 Redis 客户端。
    2021-07-07

最新评论