其他分享
首页 > 其他分享> > Go的json解析:Marshal与Unmarshal

Go的json解析:Marshal与Unmarshal

作者:互联网

Go的json解析:Marshal与Unmarshal

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package main   import (     "encoding/json"     "fmt" )   type Stu struct {     Name  string `json:"name"`     Age   int     HIgh  bool     sex   string     Class *Class `json:"class"` }   type Class struct {     Name  string     Grade int }   func main() {     //实例化一个数据结构,用于生成json字符串     stu := Stu{         Name: "张三",         Age:  18,         HIgh: true,         sex:  "男",     }       //指针变量     cla := new(Class)   //这个new方法,就相当于  cla := &Class{},是一个取地址的操作。     cla.Name = "1班"     cla.Grade = 3     stu.Class = cla       //Marshal失败时err!=nil     jsonStu, err := json.Marshal(stu)     if err != nil {         fmt.Println("生成json字符串错误")     }       //jsonStu是[]byte类型,转化成string类型便于查看     fmt.Println(string(jsonStu)) }   //打印效果: {"name":"张三","Age":18,"HIgh":true,"class":{"Name":"1班","Grade":3}} 从结果中可以看出   只要是可导出成员(变量首字母大写),都可以转成json。因成员变量sex是不可导出的,故无法转成json。   如果变量打上了json标签,如Name旁边的 `json:"name"` ,那么转化成的json key就用该标签“name”,否则取变量名作为key,如“Age”,“HIgh”。   bool类型也是可以直接转换为json的value值。Channel, complex 以及函数不能被编码json字符串。当然,循环的数据结构也不行,它会导致marshal陷入死循环。   指针变量,编码时自动转换为它所指向的值,如cla变量。 (当然,不传指针,Stu struct的成员Class如果换成Class struct类型,效果也是一模一样的。只不过指针更快,且能节省内存空间。)   最后,强调一句:json编码成字符串后就是纯粹的字符串了。

  

  上面的成员变量都是已知的类型,只能接收指定的类型,比如string类型的Name只能赋值string类型的数据。 但有时为了通用性,或使代码简洁,我们希望有一种类型可以接受各种类型的数据,并进行json编码。这就用到了interface{}类型。   前言: interface{}类型其实是个空接口,即没有方法的接口。go的每一种类型都实现了该接口。因此,任何其他类型的数据都可以赋值给interface{}类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package main   type Stu struct {     Name  interface{} `json:"name"`     Age   interface{}     HIgh  interface{}     sex   interface{}     Class interface{} `json:"class"` }   type Class struct {     Name  string     Grade int }   func main() {     //实例化一个数据结构,用于生成json字符串     stu := Stu{         Name: "张三",         Age:  18,         HIgh: true,         sex:  "男",     }       //指针变量     cla := new(Class)       cla.Name = "1班"     cla.Grade = 3     stu.Class = cla       //Marshal失败时err!=nil     jsonStu, err := json.Marshal(stu)     if err != nil {         fmt.Println("生成json字符串错误")     }       //jsonStu是[]byte类型,转化成string类型便于查看     fmt.Println(string(jsonStu)) } //打印效果 //{"name":"张三","Age":18,"HIgh":true,"class":{"Name":"1班","Grade":3}} //从结果中可以看出,无论是string,int,bool,还是指针类型等,都可赋值给interface{}类型,且正常编码,效果与前面的例子一样。

  

补充: 在实际项目中,编码成json串的数据结构,往往是切片类型。如下定义了一个[]StuRead类型的切片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package main   import (    "encoding/json"    "fmt" )   func main() {    type StuRead struct {       Name  interface{} `json:"name"`       Age   interface{}       HIgh  interface{}       sex   interface{}       Class interface{} `json:"class"`       Test  interface{}    }        //方式1:只声明,不分配内存    var stus1 []*StuRead      //方式2:分配初始值为0的内存    stus2 := make([]*StuRead,0)      //错误示范    //new()只能实例化一个struct对象,而[]StuRead是切片,不是对象    stus := new([]StuRead)      stu1 := &StuRead{"asd1",1,1,1,1,1}    stu2 := &StuRead{"asd2",2,2,2,2,2}      //由方式1和2创建的切片,都能成功追加数据    //方式2最好分配0长度,append时会自动增长。反之指定初始长度,长度不够时不会自动增长,导致数据丢失    stus1 = append(stus1,stu1)  //因为上面stus1是切片类型的结构体指针类型,所以append的类型也必须是取的地址。    stus2 = append(stus2,stu2)  //因为上面stus2是切片类型的结构体指针类型,所以append的类型也必须是取的地址。      //成功编码    json1,_ := json.Marshal(stus1)    json2,_ := json.Marshal(stus2)    fmt.Println(string(json1))    fmt.Println(string(json2)) } //打印效果 [{"name":"asd1","Age":1,"HIgh":1,"class":1,"Test":1}] [{"name":"asd2","Age":2,"HIgh":2,"class":2,"Test":2}]

  解码时定义对应的切片接受即可

Json Unmarshal:将json字符串解码到相应的数据结构

我们将上面的例子进行解码

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 type StuRead struct {     Name  interface{} `json:"name"`     Age   interface{}     HIgh  interface{}     sex   interface{}     Class interface{} `json:"class"`     Test  interface{} }   type Class struct {     Name  string     Grade int }   func main() {     //json字符中的"引号,需用\进行转义,否则编译出错     //json字符串沿用上面的结果,但对key进行了大小的修改,并添加了sex数据     data:="{\"name\":\"张三\",\"Age\":18,\"high\":true,\"sex\":\"男\",\"CLASS\":{\"naME\":\"1班\",\"GradE\":3}}"     str:=[]byte(data)       //1.Unmarshal的第一个参数是json字符串,第二个参数是接受json解析的数据结构。     //第二个参数必须是指针,否则无法接收解析的数据,如stu仍为空对象StuRead{}     //2.可以直接stu:=new(StuRead),此时的stu自身就是指针     stu:=StuRead{}     err:=json.Unmarshal(str,&stu)       //解析失败会报错,如json字符串格式不对,缺"号,缺}等。     if err!=nil{         fmt.Println(err)     }       fmt.Println(stu) }   //打印效果 {张三 18 true <nil> map[naME:1班 GradE:3] <nil>}

  总结:

json字符串解析时,需要一个“接收体”(也就是Unmarshal的第二个参数)接受解析后的数据,且Unmarshal时接收体必须传递指针。否则解析虽不报错,但数据无法赋值到接受体中。如这里用的是StuRead{}接收,就无法接收数据。   解析时,接收体可自行定义。json串中的key自动在接收体中寻找匹配的项进行赋值。匹配规则是:           没有json标签的,就从上往下依次查找变量名与key一样的变量,如Age。或者变量名忽略大小写后与key一样的变           量。如HIgh,Class。第一个匹配的就赋值,后面就算有匹配的也忽略。         (前提是该变量必需是可导出的,即首字母大写)。         不可导出的变量无法被解析(如sex变量,虽然json串中有key为sex的k-v,解析后其值仍为nil,即空值)    

让我们看一下这几个interface{}变量解析后的类型:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 func main() {     //与前边json解析的代码一致     ...     fmt.Println(stu) //打印json解析前变量类型     err:=json.Unmarshal(str,&stu)     fmt.Println("--------------json 解析后-----------")     ...     fmt.Println(stu) //打印json解析后变量类型    }   //利用反射,打印变量类型 func printType(stu *StuRead){     nameType:=reflect.TypeOf(stu.Name)     ageType:=reflect.TypeOf(stu.Age)     highType:=reflect.TypeOf(stu.HIgh)     sexType:=reflect.TypeOf(stu.sex)     classType:=reflect.TypeOf(stu.Class)     testType:=reflect.TypeOf(stu.Test)       fmt.Println("nameType:",nameType)     fmt.Println("ageType:",ageType)     fmt.Println("highType:",highType)     fmt.Println("sexType:",sexType)     fmt.Println("classType:",classType)     fmt.Println("testType:",testType) }   //结果 nameType: <nil> ageType: <nil> highType: <nil> sexType: <nil> classType: <nil> testType: <nil> --------------json 解析后----------- nameType: string ageType: float64 highType: bool sexType: <nil> classType: map[string]interface {} testType: <nil>

  

从结果中可见
1 2 “简单数据”:是指不能再进行二次json解析的数据,如”name”:”张三”只能进行一次json解析。 “复合数据”:类似”CLASS\”:{\”naME\”:\”1班\”,\”GradE\”:3}这样的数据,是可进行二次甚至多次json解析的,因为它的value也是个可被解析的独立json。即第一次解析key为CLASS的value,第二次解析value中的key为naME和GradE的value

 

1 2 3 4 5 6 7 type StuRead struct { ... //普通struct类型 Class Class `json:"class"` //指针类型 Class *Class `json:"class"` }
1 // 打印效果
Class类型:{张三 18 true <nil> {1班 3} <nil>} *Class类型:{张三 18 true <nil> 0xc42008a0c0 <nil>}

标签:stu,interface,json,类型,Go,解析,Unmarshal,Class
来源: https://www.cnblogs.com/ithubb/p/13550551.html