乐趣区

关于后端:Go如何解析不定JSON数据

博客:cbb777.fun

全平台账号: 安妮的心动录

github: https://github.com/anneheartrecord

下文中我说的可能对,也可能不对,鉴于笔者程度无限,请君自辨。有问题欢送大家找我探讨

在开发中经常会碰到很多 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 {// 解决年龄为其余类型或不存在的状况}

须要留神的是:类型断言的底层为反射,因为在运行时须要判断一个接口值的具体类型,而这个类型是在编译时无奈确定的,须要在运行时动静地获取。效率比失常的代码低一到两个数量级,而且须要耗费额定的工夫和内存

本文由 mdnice 多平台公布

退出移动版