Go json

2021-01-06  本文已影响0人  JunChow520

JSON虽是JavaScript的一个子集,但采用完全独立于编程语言的文本格式,表现为键值对集合(字典结构)的文本描述形式,使其成为较为理想、跨平台、跨语言的数据交换语言。

JSON格式可有效地提升网络传输效率,网络传输时会先将数据序列化为JSON字符串,接收方再反序列化为对应的数据类型。

encoding/json

import "encoding/json"

JSON格式隶属于序列化格式,Go语言的强类型对格式要求严格,JSON格式虽然有类型但并不稳定。

encoding/json

流式读写Stream

json.NewEncoder

func NewEncoder(w io.Writer) *Encoder {
    return &Encoder{w: w, escapeHTML: true}
}

json.NewDecoder

func NewDecoder(r io.Reader) *Decoder {
    return &Decoder{r: r}
}

strings.NewReader

func strings.NewReader(s string) *strings.Reader { 
  return &Reader{s, 0, -1} 
}

strings.NewReader()会创建一个从字符串中读取数据的读取器strings.Reader,与strings.Builder不同的是strings.Reader类型是为了高效读取字符串而存在的。

json.Decoder

例如:

type User struct{
    Id int `json:"id"`
    Name string `json:"name"`
}
jsonstr := `{"id":1, "name":"root"}`
reader := strings.NewReader(jsonstr)
decoder := json.NewDecoder(reader)

var user User
err := decoder.Decode(&user)
if err!=nil {
    panic(err)
}

fmt.Printf("%#v\n", user)
main.User{Id:1, Name:"root"}

json.Encoder

user := User{Id:1, Name:"root"}

buf := new(bytes.Buffer)
err := json.NewEncoder(buf).Encode(user)
if err!=nil {
    panic(err)
}

fmt.Printf("%s\n", buf)

json.Marshal

func json.Marshal(v interface{}) ([]byte, error)

数据类型编码注意事项

Go JSON
struct/*struct Object
array/slice Array
map Object

Golang多数数据类型都可以转换为有效的JSON文本,但channel通道、complex、函数等类型除外。若转换前数据类型中出现指针则会转换指针所指向的指,若指针指向的是零值,则会将nil转换为结果。

type User struct{
    Id int `json:"id"`
    Name string `json:"name"`
    Colors []string `json:"colors"`
}

user := User{Id:1, Name:"root", Colors:[]string{"Red", "Green", "Blue", "Ruby"}}

buf := new(bytes.Buffer)
err := json.NewEncoder(buf).Encode(user)
if err!=nil {
    panic(err)
}

fmt.Printf("%s\n", buf)
{"id":1,"name":"root","colors":["Red","Green","Blue","Ruby"]}

通过对数据进行编码或序列化生成JSON格式

结构体编码生成JSON

使用json.Marshal()函数将结构实例生成JSON格式的文本

package main
import (
    "fmt"
    "encoding/json"
)

type User struct{
    Id int
    UserName string
    Active bool
    Balance float32
}

func main () {
    user := User{1, "admin", true, 1000.01}
    data, err := json.Marshal(user)
    if err!= nil{
        fmt.Printf("Error: %v\n", err)
    }
    
    fmt.Printf("%T %v\n", data, data)

    jsonstr := string(data)
    fmt.Printf("%T %v\n", jsonstr, jsonstr)
}
[]uint8 [123 34 73 100 34 58 49 44 34 85 115 101 114 78 97 109 101 34 58 34 97 100 109 105 110 34 44 34 65 99 116 105 118 101 34 58 116 114 117 101 44 34 66 97 108 97 110 99 101 34 58 49 48 48 48 46 48 49 125]
string {"Id":1,"UserName":"admin","Active":true,"Balance":1000.01}

json.Marshal()编码成功返回err将会被赋予零值nildata则是一个进行JSON格式化后的[]byte字节切片类型的变量。

调用json.Marshal()时会递归实例,若实例实现了json.Marshaler接口且包含有效值,Marshal()函数就会调用其MarshalJSON()方法将该数据结构转换为JSON格式的文本。

Go语言中大多数据类型都可以转化为有效地JSON文本,但channel通道、complex复数、function函数这几种类型除外。

字典映射生成JSON

package main
import (
    "fmt"
    "encoding/json"
)

func main () {

    dict := make(map[string]interface{})
    dict["id"] = 1
    dict["username"] = "admin"
    dict["active"] = true
    dict["balance"] = 1000.99
    dict["operator"] = []string{"root", "tester"}

    data, _ := json.MarshalIndent(dict, "", "    ")
    jsonstr := string(data)

    fmt.Printf("%T %v\n", jsonstr, jsonstr)
}
string {
    "active": true,
    "balance": 1000.99,
    "id": 1,
    "operator": [
        "root",
        "tester"
    ],
    "username": "admin"
}

转换前后数据类型

GO语言中JSON转换前后数据类型映射关系

转换前 转换后 描述
布尔值 布尔类型 -
浮点数/整数 常规数字 -
字符串 字符串 UTF-8编码字符串会被转换为Unicode字符集的字符串,特殊字符会转码,比如<会被转换为\u003c
数组 JSON数组 []byte类型的值会被转换为Base64编码后的字符串。
切片 JSON数组 切片类型的零值会被转换为null
结构体 JSON对象 只有结构体中以大写字母开头且可以被导出的字段才能被转换输出。
映射 JSON格式 只有数据类型为map[string]TTencoding/json库支持的任意数据类型才能转换。
package main
import (
    "fmt"
    "encoding/json"
)

func main () {
    user := struct{
        Id int32
        UserName string
        Active bool
        Scores map[string]int32
    }{
        1,
        "admin",
        false,
        map[string]int32{"Music":90, "English":100},
    }
    
    data, _ := json.Marshal(user)
    jsonstr := string(data)

    fmt.Printf("%T %v\n", jsonstr, jsonstr)
}
string {"Id":1,"UserName":"admin","Active":false,"Scores":{"English":100,"Music":90}}

结构体字段中若首字母为小写则JSON无权访问,输出的字段首字母全部为大写。若需要输出自定义的阶段名,比如小写,就需要使用结构体Tag标签。

package main
import (
    "fmt"
    "encoding/json"
)

func main () {
    user := struct{
        Id int32 `json:"id"`
        UserName string `json:"username"`
        Active bool `json:"active"`
        Scores map[string]int32 `json:"score_set"`
        Operator []string `json:"operator"`
    }{
        1,
        "admin",
        false,
        map[string]int32{"Music":90, "English":100},
        []string{"root", "tester"},
    }

    data, _ := json.Marshal(user)
    jsonstr := string(data)

    fmt.Printf("%T %v\n", jsonstr, jsonstr)
}
string {"id":1,"username":"admin","active":false,"score_set":{"English":100,"Music":90},"operator":["root","tester"]}

StructTag

`标签类型:标签名称`
ProductID int64   `json:"-"` // 表示不进行序列化
Name      string  `json:"name"`
IsOnSale  bool    `json:"is_on_sale,omitempty"`
Price     float64 `json:"price,string"`

JSON序列化或反序列化时,结构体类型和目标类型可能不一致,此时可在标签中指定stringnumberboolean三种类型。

package main
import (
    "fmt"
    "encoding/json"
)

func main () {
    user := struct{
        Id int32 `json:"id,string"`
        UserName string `json:"username"`
        Active bool `json:"active"`
        Scores map[string]int32 `json:"score_set"`
        Operator []string `json:"operator"`
        Company string `json:"-"`
        Balance float32 `json:"balace,omitempty"`
    }{
        1,
        "admin",
        false,
        map[string]int32{"Music":90, "English":100},
        []string{"root", "tester"},
        "BOSS Company",
        0.0,
    }

    bs, err := json.MarshalIndent(user, "", "    ")
    jsonstr := string(bs)

    fmt.Printf("%T %v\n", jsonstr, jsonstr)
}
string {
    "id": "1",
    "username": "admin",
    "active": false,
    "score_set": {
        "English": 100,
        "Music": 90
    },
    "operator": [
        "root",
        "tester"
    ]
}

json.MarshalIndent

func json.MarshalIndex(v interface{}, prefix, indent string) ([]byte, error)

例如:

bs, err := json.MarshalIndent(user, "", "    ")
string {
    "id": 1,
    "username": "admin",
    "active": false,
    "score_set": {
        "English": 100,
        "Music": 90
    },
    "operator": [
        "root",
        "tester"
    ]
}

json.Unmarshal

json.Unmarshal()可将JSON格式文本转换为Go语言中预置的数据结构。

func json.Unmarshal(data []byte, v interface{}) error
参数 类型 描述
data []byte JSON格式文本
v interface{} 目标输出容器用于存放解码后的值

JSON解码之前需要在Go中创建一个目标类型的实例用来存放解码后的值,若JSON格式文本能与实例结构对应则解码后的值会被存放到实例中。

Unmarshal()会根据约定的顺序查找目标结构中的字段,若找到即发生匹配。

  1. 从结构体标签中查找包含目标结构的字段
  2. 从结构体字段中查找目标结构的字段
  3. 与目标结构名称相同的字段或除首字母大写外其它字母不区分大小写命令的字段。

如果JSON中字段在Go目标类型中不存在则json.Unmarshal()在解码时会丢弃该字段。

Go语言在解析JSON数据时,需要提前根据JSON的数据结构,预先定义好对应数据类型才能存储解析后的结果。一般会使用结构体与切片来定义用于接收JSON解析后的数据类型。

package main
import (
    "fmt"
    "encoding/json"
)

func main () {
    jsonStr := `{"id":1, "name":"admin", status:true, balance:100.99}`
    jsonBytes := []byte(jsonStr)
 
    type User struct{
        Id int
        Name string
        Status bool
        Balance float64
    }
    user := User{}

    err := json.Unmarshal(jsonBytes, &user)
    fmt.Println(err)//invalid character 's' looking for beginning of object key string
    fmt.Println(user)//{0  false 0}
}

注意JSON格式字符串每个键都必须是字符串

{"id":1, "name":"admin", "status":true, "balance":100.99}

json.Valid

encoding/json包中提供了json.Valid()方法用于验证JSON格式的对象和数组是否正确。

package main
import (
    "fmt"
    "encoding/json"
)


func main () {
    jsonStr := `{"id":1, "name":"admin", "status":true, "balance":100.99}`
    jsonBytes := []byte(jsonStr)
    ok := json.Valid(jsonBytes)
    fmt.Println(ok)//true
    if !ok{
        fmt.Println("json pattern error")
        return
    }
 
    type User struct{
        Id int64
        Name string
        Status bool
        Balance float64
    }
    user := User{}

    err := json.Unmarshal(jsonBytes, &user)
    fmt.Println(err)//
    fmt.Println(user)//{1 admin true 100.99}
}

Go语言内建灵活的类型系统,其中空接口interface{}是一个通用类型。当需要解码一段未知的JSON格式文本时,只需要将其解码输入出到一个空接口即可。

Go标准库encoding/json允许使用map[string]interface{}[]interface{}类型的值来分别存放未知结构的JSON对象或数组。

定义空接口后,使用json.Unmarshal()会将JSON对象解码到空接口中,最终空接口将会是一个键值对的map[string] interface{}结构。

package main
import (
    "fmt"
    "encoding/json"
)

func main () {

    jsonstr := []byte(`{"id":1, "username":"admin", "status":true, "balance":1000.99}`)
    fmt.Printf("%T\n", jsonstr)//[]uint8
    fmt.Printf("%v\n", jsonstr)//[123 34 105 100 34 58 49 44 32 34 117 115 101 114 110 97 109 101 34 58 34 97 100 109 105 110 34 44 32 34 115 116 97 116 117 115 34 58 116 114 117 101 44 32 34 98 97 108 97 110 99 101 34 58 49 48 48 48 46 57 57 125]

    var v interface{}
    err := json.Unmarshal(jsonstr, &v)
    if err != nil{
        fmt.Printf("error: %v\n", err)
    }

    fmt.Printf("%T\n", v)//map[string]interface {}
    fmt.Printf("%v\n", v)//map[id:1 username:admin status:true balance:1000.99]
}

若需访问解码后的数据结构首先需要判断目标结构是否为预期的数据结构。

//判断目标结构是否为预期的数据结构
data,ok := v.(map[string]interface{})
if !ok{
 fmt.Printf("json decode fail\n")
}
fmt.Printf("%T\n", data)//map[string]interface {}
fmt.Printf("%v\n", data)//map[id:1 username:admin status:true balance:1000.99]

JSON解码过程中元素类型之间的转换关系

JSON Go
布尔值 bool
数值 float64
字符串 string
JSON数组 []interface{}
JSON对象 map[string]interface{}
null nil

通过for循环搭配range语句遍历访问解码后的目标数据

//使用for循环配合range语句遍历访问解码后的目标数据
for k,v := range data{
    switch val := v.(type){
        case string:
            fmt.Println(k, v, val)
        case int:
            fmt.Println(k, v, val)
        case float64:
            fmt.Println(k, v, val)
        case bool:
            fmt.Println(k, v, val)
        case []interface{}:
            fmt.Println(k, v, val)
            for i, iv := range val{
                fmt.Println(i, iv)
            }
        default:
            fmt.Println(k, v, val, "cannot hanle yet")
    }
}

需要注意的是JSON中的整数或小数解码后都会转换为float64浮点数

package main
import (
    "fmt"
    "encoding/json"
    "reflect"
)

func main () {
    type User struct{
        Id int64
        Name string
        Balance float64
    }

    user :=  User{Id:1, Name:"admin", Balance:100.99}
    jsonBytes, _ := json.Marshal(user)
    jsonStr := string(jsonBytes)
    fmt.Println(jsonStr)//{"Id":1,"Name":"admin","Balance":100.99}

    var iuser interface{}
    json.Unmarshal(jsonBytes, &iuser)
    jsonMap := iuser.(map[string]interface{})
    fmt.Println(jsonMap)//map[Id:1 Name:admin Balance:100.99]

    fmt.Printf("Id Type is %s\n", reflect.TypeOf(jsonMap["Id"]).Name())//Id Type is float64
}

json.RawMessage

上一篇下一篇

猜你喜欢

热点阅读