zoukankan      html  css  js  c++  java
  • go学习-结构体与JSON序列化

    结构体与JSON序列化

    JSON数据与结构体之间相互转换
    基本的序列化:json.Marshal()(序列化:结构体-->JSON格式的字符串)与json.Unmarshal(反序列化:JSON格式的字符串-->结构体)

    package main
    
    import ("fmt"
    "encoding/json"
    )
    
    //Student 学生
    type Student struct {
    	ID     int
    	Gender string
    	Name   string
    }
    
    //Class 班级
    type Class struct {
    	Title    string
    	Students []*Student
    }
    
    func main() {
    	c := &Class{
    		Title:    "101",
    		Students: make([]*Student, 0, 200),
    	}
    	for i := 0; i < 10; i++ {
    		stu := &Student{
    			Name:   fmt.Sprintf("stu%02d", i),
    			Gender: "男",
    			ID:     i,
    		}
    		c.Students = append(c.Students, stu)
    	}
    	//JSON序列化:结构体-->JSON格式的字符串
    	data, err := json.Marshal(c)
    	if err != nil {
    		fmt.Println("json marshal failed")
    		return
    	}
    	fmt.Printf("json序列化结果:%s
    ", data)
    	//JSON反序列化:JSON格式的字符串-->结构体
    	str := `{"Title":"101","Students":[{"ID":0,"Gender":"男","Name":"stu00"},{"ID":1,"Gender":"男","Name":"stu01"},{"ID":2,"Gender":"男","Name":"stu02"},{"ID":3,"Gender":"男","Name":"stu03"},{"ID":4,"Gender":"男","Name":"stu04"},{"ID":5,"Gender":"男","Name":"stu05"},{"ID":6,"Gender":"男","Name":"stu06"},{"ID":7,"Gender":"男","Name":"stu07"},{"ID":8,"Gender":"男","Name":"stu08"},{"ID":9,"Gender":"男","Name":"stu09"}]}`
    	c1 := &Class{}
    	err = json.Unmarshal([]byte(str), c1)
    	if err != nil {
    		fmt.Println("json unmarshal failed!")
    		return
    	}
    	fmt.Printf("json反序列化结果:%#v
    ", c1)
    }
    

    结构体标签(Tag)

    Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。 Tag在结构体字段的后方定义,由一对反引号包裹起来,
    具体的格式如下:
    key1:"value1" key2:"value2"

    注意事项: 为结构体编写Tag时,必须严格遵守键值对的规则。结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。例如不要在key和value之间添加空格。

    使用json tag指定字段名

    序列化与反序列化默认情况下使用结构体的字段名,我们可以通过给结构体字段添加tag来指定json序列化生成的字段名。

    package main
    
    import ("fmt"
    "encoding/json"
    )
    
    // 使用json tag指定序列化与反序列化时的行为
    //Student 学生
    type Student struct {
    	ID     int    `json:"id"` //通过指定tag实现json序列化该字段时的key,指定json序列化/反序列化时使用小写id
    	Gender string //json序列化是默认使用字段名作为key
    	name   string //私有不能被json包访问
    }
    func main() {
    	s1 := Student{
    		ID:     1,
    		Gender: "男",
    		name:   "沙河娜扎",
    	}
    	data, err := json.Marshal(s1)
    	if err != nil {
    		fmt.Println("json marshal failed!")
    		return
    	}
    	fmt.Printf("json str:%s
    ", data) //json str:{"id":1,"Gender":"男"}
    	fmt.Printf("%#v
    ", s1) //main.Student{ID:1, Gender:"男", name:"沙河娜扎"}
    }
    

    忽略某个字段

    如果你想在json序列化/反序列化的时候忽略掉结构体中的某个字段,可以按如下方式在tag中添加-。

    // 使用json tag指定json序列化与反序列化时的行为
    type Person struct {
    	Name   string `json:"name"` // 指定json序列化/反序列化时使用小写name
    	Age    int64
    	Weight float64 `json:"-"` // 指定json序列化/反序列化时忽略此字段
    }
    
    忽略空值字段

    当 struct 中的字段没有值时, json.Marshal() 序列化的时候不会忽略这些字段,而是默认输出字段的类型零值(例如int和float类型零值是 0,string类型零值是"",对象类型零值是 nil)。
    如果想要在序列序列化时忽略这些没有值的字段时,可以在对应字段添加omitempty tag。
    例如:没有添加omitempty

    package main
    
    import ("fmt"
    "encoding/json"
    )
    
    type User struct {
    	Name  string   `json:"name"`
    	Email string   `json:"email"`
    	Hobby []string `json:"hobby"`
    }
    func main() {
    	u1 := User{
    		Name: "七米",
    	}
    	// struct -> json string
    	b, err := json.Marshal(u1)
    	if err != nil {
    		fmt.Printf("json.Marshal failed, err:%v
    ", err)
    		return
    	}
    	fmt.Printf("str:%s
    ", b)
    }
    

    添加omitempty:序列化结果中没有email和hobby字段

    package main
    
    import ("fmt"
    "encoding/json"
    )
    
    type User struct {
    	Name  string   `json:"name,omitempty"`
    	Email string   `json:"email,omitempty"`
    	Hobby []string `json:"hobby,omitempty"`
    }
    func main() {
    	u1 := User{
    		Name: "七米",
    	}
    	// struct -> json string
    	b, err := json.Marshal(u1)
    	if err != nil {
    		fmt.Printf("json.Marshal failed, err:%v
    ", err)
    		return
    	}
    	fmt.Printf("str:%s
    ", b)
    }
    

    结构体和方法补充知识点

    因为slice和map这两种数据类型都包含了指向底层数据的指针,因此我们在需要复制它们时要特别注意。

    package main
    
    import ("fmt"
    )
    
    type Person struct {
    	name   string
    	age    int8
    	dreams []string
    }
    func (p *Person) SetDreams(dreams []string) {
    	p.dreams = dreams
    }
    
    func main() {
    	p1 := Person{name: "小王子", age: 18}
            p2 := Person{name: "小王子2", age: 20}
    	data := []string{"吃饭", "睡觉", "打豆豆"}
    	p1.SetDreams(data)
            // 你真的想要修改 p1.dreams 吗?
    	data[1] = "不睡觉"
    	p2.SetDreams(data)
    	
    	fmt.Println(p1.dreams)  // 不想改变
            fmt.Println(p2.dreams)  // 想改变
    }
    

    结果:p1的内容也改变了。而我们真正想改变的只有p2的内容,不想改变p1

    正确的做法是在方法中使用传入的slice的拷贝进行结构体赋值。
    这样就会只改变副本,而不会全部改变
    原因:由于切片和map是引用类型,所以a和b其实都指向了同一块内存地址。修改b的同时a的值也会发生变化。
    Go语言内建的copy()函数可以迅速地将一个切片的数据复制到另外一个切片空间中,改变一个值,另一个的值就不会变化了。因为地址不同了。

    func (p *Person) SetDreams(dreams []string) {
    	p.dreams = make([]string, len(dreams))//动态创建长度为dreams的切片
    	copy(p.dreams, dreams)//将dreams拷贝到p.dreams(另一个空间,不共用一个地址)
    }
    
    

    所有博客均为自己学习的笔记。如有错误敬请理解。
  • 相关阅读:
    页面嵌入QQ功能(点QQ建立一个临时会话,显示在页面的固定位置)
    mavn项目(springMVC) 引入静态资源(js、css)等
    java 信号量Semaphore
    无法访问windows安装服务。发生这种情况的可能是您在安全模式下运行windows,或是没有正确安装windows安装,。请与技术支持人员联系以获得帮助。
    IIS “另一个程序正在使用此文件,进程无法访问"
    无需写try/catch,也能正常处理异常
    不修改代码就能优化ASP.NET网站性能的一些方法
    Web性能优化:图片优化
    毕业论文参考文献规范格式
    分布式网格缓存Coherence简介
  • 原文地址:https://www.cnblogs.com/tangtang-benben/p/15075815.html
Copyright © 2011-2022 走看看