Go JSON

JSON(JavaScript Object Notation)是一种比XML更为轻量的数据交换格式,易于人阅读与编写,也易于程序生成和解析。

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

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

Go语言完成JSON编码和解码时遵循RFC4627标准协议。

Go语言内置的encoding/json标准库对JSON格式支持,以实现生成和解析JSON格式的数据据。

import "encoding/json"

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

GO语言中对JSON编码或解码时中间数据状态为字节数组[]byte

编码

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

  • 对结构体struct编码生成JSON
  • 对映射map编码生成JSON

类型编码

GoJSON
结构体/结构体指针JSON对象(Object)
数组/切片JSON数组(Array)
字典JSON对象(Object)

encoding/json标准库提供的json.Marshal()函数可对一组数据进行JSON格式的编码。

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

结构体编码生成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"]}

结构体标签Tag

Tag也就是给结构体字段打标签,标签冒号前为类型,冒号后为标签名。

`标签类型:标签名称`
  • 结构体字段标签为-短横线表示不输出到JSON
  • 结构体字段标签带有自定义名称时,自定义名称会出现在JSON的字段名中。
  • 结构体字段标签中若带有omitempty选项则当该字段为空或为0时不会输入到JSON字符串中
  • 结构体字段类型为boolstringintint64等,标签中却带有,string选项,表示该字段输出到JSON时会自动转换为JSON字符串。

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,
    }

    data, _ := json.MarshalIndent(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"
    ]
}

encoding/json库中提供了json.MarshalIndent()方法用于格式化输出,主要使用缩进对输出进行格式化。

func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

例如:

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

解码

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

func Unmarshal(data []byte, v interface{}) error

Unmarshal()函数的第一个参数是输入的JSON格式文本(比特序列),第二个参数表示目标输出容器用于存放解码后的值。

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}

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}
}

解码未知结构的JSON数据

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解码过程中元素类型之间的转换关系

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

通过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
}

流式读写

Go语言内置的encoding/json库提供了EncoderDecoder两个类型用于支持JSON数据的流式,同时提供了NewEncoder()NewDecoder()函数具体实现。

使用EncoderDecoder可以对数据流进行处理,比如读写HTTP连接、WebSocket或文件,Go语言标准库中的net/rpc/jsonrpc即应用了EncoderDecoder

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值