老男孩教育专注IT教育10余年,只培养IT技术精英

全国免费咨询电话(渠道合作):400-609-2893

GO语言学习系列之json交换格式!

老男孩IT教育

技术博客

2021年1月11日 16:10

JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。

  JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

  Go语言对json的解析函数在encoding/json包里面,主要是编码和解码两个函数。

  Marshal函数

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

  Marshal函数返回v的json编码。

  Marshal函数会递归的处理值。如果一个值实现了Marshaler接口切非nil指针,会调用其MarshalJSON方法来生成json编码。nil指针异常并不是严格必需的,但会模拟与UnmarshalJSON的行为类似的必需的异常。

  否则,Marshal函数使用下面的基于类型的默认编码格式:

  布尔类型编码为json布尔类型。

  浮点数、整数和Number类型的值编码为json数字类型。

  字符串编码为json字符串。角括号"<"和">"会转义为"\u003c"和"\u003e"以避免某些浏览器吧json输出错误理解为HTML。基于同样的原因,"&"转义为"\u0026"。

  数组和切片类型的值编码为json数组,但[]byte编码为base64编码字符串,nil切片编码为null。

  结构体的值编码为json对象。每一个导出字段变成该对象的一个成员,除非:

  - 字段的标签是"-"- 字段是空值,而其标签指定了omitempty选项

  空值是false、0、""、nil指针、nil接口、长度为0的数组、切片、映射。对象默认键字符串是结构体的字段名,但可以在结构体字段的标签里指定。结构体标签值里的"json"键为键名,后跟可选的逗号和选项,举例如下:

// 字段被本包忽略
Field int `json:"-"`
// 字段在json里的键为"myName"
Field int `json:"myName"`
// 字段在json里的键为"myName"且如果字段为空值将在对象中省略掉
Field int `json:"myName,omitempty"`
// 字段在json里的键为"Field"(默认值),但如果字段为空值会跳过;注意前导的逗号
Field int `json:",omitempty"`

  "string"选项标记一个字段在编码json时应编码为字符串。它只适用于字符串、浮点数、整数类型的字段。这个额外水平的编码选项有时候会用于和javascript程序交互:

Int64String int64 `json:",string"`

  如果键名是只含有unicode字符、数字、美元符号、百分号、连字符、下划线和斜杠的非空字符串,将使用它代替字段名。

  匿名的结构体字段一般序列化为他们内部的导出字段就好像位于外层结构体中一样。如果一个匿名结构体字段的标签给其提供了键名,则会使用键名代替字段名,而不视为匿名。

  Go结构体字段的可视性规则用于供json决定那个字段应该序列化或反序列化时是经过修正了的。如果同一层次有多个(匿名)字段且该层次是最小嵌套的(嵌套层次则使用默认go规则),会应用如下额外规则:

  1)json标签为"-"的匿名字段强行忽略,不作考虑;

  2)json标签提供了键名的匿名字段,视为非匿名字段;

  3)其余字段中如果只有一个匿名字段,则使用该字段;

  4)其余字段中如果有多个匿名字段,但压平后不会出现冲突,所有匿名字段压平;

  5)其余字段中如果有多个匿名字段,但压平后出现冲突,全部忽略,不产生错误。

  对匿名结构体字段的管理是从go1.1开始的,在之前的版本,匿名字段会直接忽略掉。

  映射类型的值编码为json对象。映射的键必须是字符串,对象的键直接使用映射的键。

  指针类型的值编码为其指向的值(的json编码)。nil指针编码为null。

  接口类型的值编码为接口内保持的具体类型的值(的json编码)。nil接口编码为null。

  通道、复数、函数类型的值不能编码进json。尝试编码它们会导致Marshal函数返回UnsupportedTypeError。

  Json不能表示循环的数据结构,将一个循环的结构提供给Marshal函数会导致无休止的循环。

  Unmarshal函数

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

  Unmarshal函数解析json编码的数据并将结果存入v指向的值。

  Unmarshal和Marshal做相反的操作,必要时申请映射、切片或指针,有如下的附加规则:

  要将json数据解码写入一个指针,Unmarshal函数首先处理json数据是json字面值null的情况。此时,函数将指针设为nil;否则,函数将json数据解码写入指针指向的值;如果指针本身是nil,函数会先申请一个值并使指针指向它。

  要将json数据解码写入一个结构体,函数会匹配输入对象的键和Marshal使用的键(结构体字段名或者它的标签指定的键名),优先选择精确的匹配,但也接受大小写不敏感的匹配。

  要将json数据解码写入一个接口类型值,函数会将数据解码为如下类型写入接口:

Bool                   对应JSON布尔类型
float64                对应JSON数字类型
string                 对应JSON字符串类型
[]interface{}          对应JSON数组
map[string]interface{} 对应JSON对象
nil                    对应JSON的null

  如果一个JSON值不匹配给出的目标类型,或者如果一个json数字写入目标类型时溢出,Unmarshal函数会跳过该字段并尽量完成其余的解码操作。如果没有出现更加严重的错误,本函数会返回一个描述第一个此类错误的详细信息的UnmarshalTypeError。

  JSON的null值解码为go的接口、指针、切片时会将它们设为nil,因为null在json里一般表示“不存在”。解码json的null值到其他go类型时,不会造成任何改变,也不会产生错误。

  当解码字符串时,不合法的utf-8或utf-16代理(字符)对不视为错误,而是将非法字符替换为unicode字符U+FFFD。

  值类型json转换

package main

import (
    "encoding/json"
    "fmt"
)

// int 类型 json转换
func JsonInt() {
    var arg = 100
    data, err := json.Marshal(arg)
    if err != nil {
        fmt.Printf("json.marshal failed, err : %v\n", err)
        return
    }
    var reply int
    err = json.Unmarshal(data, &reply)

    fmt.Printf("int 类型\nJSON序列化 string(data) => %s\nJSON反序列化 reply => %v\n", string(data), reply)
}

// string 类型 json转换
func JsonString() {
    var arg = "oldboy"
    data, err := json.Marshal(arg)
    if err != nil {
        fmt.Printf("json.marshal failed, err : %v\n", err)
        return
    }
    var reply string
    err = json.Unmarshal(data, &reply)

    fmt.Printf("string 类型\nJSON序列化 string(data) => %s\nJSON反序列化 reply => %v\n", string(data), reply)
}

// array 类型 json转换
func JsonArray() {
    var arg = [2]string{"oldboy", "Golang"}
    data, err := json.Marshal(arg)
    if err != nil {
        fmt.Printf("json.marshal failed, err : %v\n", err)
        return
    }
    var reply [2]string
    err = json.Unmarshal(data, &reply)

    fmt.Printf("array 类型\nJSON序列化 string(data) => %s\nJSON反序列化 reply => %v\n", string(data), reply)
}

func main() {
    JsonInt()
    JsonString()
    JsonArray()
}

  指针类型json转换

package main

import (
    "encoding/json"
    "fmt"
)

// slice 类型 json转换
func JsonSlice() {
    var arg = []string{"oldboy", "Linux", "Python", "Golang", "Java", "DBA"}
    data, err := json.Marshal(arg)
    if err != nil {
        fmt.Printf("json.marshal failed, err : %v\n", err)
        return
    }
    var reply []string
    err = json.Unmarshal(data, &reply)

    fmt.Printf("slice 类型\nJSON序列化 string(data) => %s\nJSON反序列化 reply => %v\n", string(data), reply)
}

// map 类型 json转换
func JsonMap() {
    var arg = map[int]string{1: "oldboy", 2: "Linux", 3: "Python", 4: "Golang", 5: "Java", 6: "DBA"}
    data, err := json.Marshal(arg)
    if err != nil {
        fmt.Printf("json.marshal failed, err : %v\n", err)
        return
    }
    var reply map[int]string = make(map[int]string, 6)
    err = json.Unmarshal(data, &reply)

    fmt.Printf("map 类型\nJSON序列化 string(data) => %s\nJSON反序列化 reply => %v\n", string(data), reply)
}

func main() {
    JsonSlice()
    JsonMap()
}

  结构体json转换

package main

import (
    "encoding/json"
    "fmt"
)

type School struct {
    Name    string `json:"name"`
    Age     int    `json:"-"`
    slogan  string `json:"slogan"`
    Courses []string
}

// struct 类型 json转换
func JsonStruct() {
    var arg = School{
        Name:    "oldboy",
        Age:     100,
        slogan:  "老男孩IT教育,只培养技术精英。",
        Courses: []string{"oldboy", "Linux", "Python", "Golang", "Java", "DBA"},
    }
    data, err := json.Marshal(arg)
    if err != nil {
        fmt.Printf("json.marshal failed, err : %v\n", err)
        return
    }
    var reply School
    err = json.Unmarshal(data, &reply)

    fmt.Printf("struct 类型\nJSON序列化 string(data) => %s\nJSON反序列化 reply => %v\n", string(data), reply)
}

func main() {
    JsonStruct()
}

  运行结果:

struct 类型
JSON序列化 arg => {"name":"oldboy","Courses":["oldboy","Linux","Python","Golang","Java","DBA"]}
JSON反序列化 reply => {oldboy 0  [oldboy Linux Python Golang Java DBA]}

  School 类型

  Name字段:JSON序列化、反序列化的时候,使用 name,而不是 Name。

  Age字段:json:"-" JSON序列化、反序列化的时候忽略该字段。默认情况下会解析这个字段,因为它是大写字母开头的。

  slogan字段:首字母为小写时,为私有字段,JSON序列化、反序列化的时候不会转换。

  Courses字段:因为它是大写字母开头的,JSON序列化、反序列化的时候默认解析这个字段。

  老男孩教育专注Linux云计算运维工程师、Python全栈+人工智能、Python自动化运维开发、网络安全、数据分析、新媒体运营、MySQLDBA开发、K8S微服务、Go语言等互联网课程培训。欢迎对it行业感兴趣的朋友们来公司考察及学习。

  推荐阅读:

  GO语言字符串有哪些?老男孩IT教育

  GO语言学习系列之Go语言面试题(二)

  Go语言常量之定义与技巧!老男孩IT教育

本文经授权发布,不代表老男孩教育立场。如若转载请联系原作者。