go语言的结构体与json字符串格式转换你会了吗?

寅恪光潜
深度学习领域优质创作者
博客专家认证
2022-11-21 10:46:04

对于结构体大家都很熟悉,是一种自定义类型,可以将不同类型的同属于这个类的属性(成员变量)集合在一起,换句话说这些成员变量属于键值对,那么这种也就是常见的json格式,我们来看下如何将结构体转成json格式的字符串。

json.Marshal转字符串

package main
 
import (
	"encoding/json"
	"fmt"
)
 
type Person struct {
	Name  string
	Tall  float32
	Hobby string
}
 
func main() {
	//实例化的时候要注意,最后的逗号不能省略
	p := Person{
		Name: "Tony", Tall: 173.5, Hobby: "Reading",
	}
	fmt.Println(p) //{Tony 173.5 Reading}
	json_p, err := json.Marshal(p)
	fmt.Printf("%T,%s,%v", string(json_p), string(json_p), err)
	//string,{"Name":"Tony","Tall":173.5,"Hobby":"Reading"},<nil>
}

如果结构体成员变量名称的首字母是小写的话,会发生什么情况?

package main
 
import (
	"encoding/json"
	"fmt"
)
 
type Person struct {
	Name    string
	Tall    float32
	Hobby   string
	address string //首字母小写
	Sex     string
}
 
func main() {
	//实例化的时候要注意,最后的逗号不能省略
	p := Person{
		Name: "Tony", Tall: 173.5, Hobby: "Reading", address: "湖南省", Sex: "男",
	}
	fmt.Println(p) //{Tony 173.5 Reading 湖南省 男}
	json_p, err := json.Marshal(p)
	fmt.Printf("%T,%s,%v", string(json_p), string(json_p), err)
	//string,{"Name":"Tony","Tall":173.5,"Hobby":"Reading","Sex":"男"},<nil>
}

我们发现这个address成员变量没有转换成功,没有出现,那说明如果需要转成json,首字母必须大写。然后我们反过来将json字符串转换成结构体,看下有什么变化。

var out Person
json.Unmarshal(json_p, &out)
fmt.Printf("%T,%#v,%v", out, out, err)
//main.Person,main.Person{Name:"Tony", Tall:173.5, Hobby:"Reading", address:"", Sex:"男"},<nil>

address的内容是空,如果是整型,内容是0,这验证了,如果外部需获取成员变量的情况,首字母需大写。

结构体嵌套

结构体是多个类型的集合,所以除了基本类型之外,自定义类型肯定也是可以一起放在里面,无差别对待使用,而结构体里面的结构体可以是指针也可以不是指针,不过建议使用指针的表示形式,这个只是一个内存地址而已,效率要快。

package main
 
import (
	"encoding/json"
	"fmt"
)
 
type Person struct {
	Name    string
	Tall    float32
	Hobby   string
	address string //首字母小写
	Sex     string
	Game    *Game //指向一个玩过什么游戏的一个结构体,也可以不使用指针,指针要快
}
type Game struct {
	Name1 string
	Name2 string
}
 
func main() {
	//实例化的时候要注意,最后的逗号不能省略
	p := Person{
		Name: "Tony", Tall: 173.5, Hobby: "Reading", address: "湖南省", Sex: "男",
	}
 
	//指针用法
	game := new(Game)
	game.Name1 = "星际争霸"
	game.Name2 = "王者荣耀"
	p.Game = game
 
	/*
		非指针用法
			game := Game{Name1: "王者荣耀", Name2: "魔兽世界"}
			p.Game = game
	*/
	json_p, err := json.Marshal(p)
	if err != nil {
		fmt.Println("转换json字符串失败!")
	}
	fmt.Printf("%T,%s", string(json_p), string(json_p))
}
//string,{"Name":"Tony","Tall":173.5,"Hobby":"Reading","Sex":"男","Game":{"Name1":"星际争霸","Name2":"王者荣耀"}}

结构体接口

 看过Go语言进阶,interface接口,socket套接字这篇文章的伙伴就应该知道,其中的 tester = p 就是将类型赋值给了接口。我们现在来改造这个结构体,使其变得更加的通用,也就是说不管什么类型都可以,而不是指定了string就只能是字符串类型。

type Person struct {
	Name    interface{}
	Tall    interface{}
	Hobby   interface{}
	address interface{} //首字母小写
	Sex     interface{}
	Game    interface{}
}

然后我们将那个Hobby爱好,原来是字符串的,我们使用整型为其赋值,试下:

p := Person{
	Name: "Tony", Tall: 173.5, Hobby: 123, address: "湖南省", Sex: "男",
}
//string,{"Name":"Tony","Tall":173.5,"Hobby":123,"Sex":"男","Game":{"Name1":"星际争霸","Name2":"王者荣耀"}}

OK,没有问题!
所有类型全部替换成interface{}空接口,因为在go语言中的每一种类型都实现了该接口,换句话说就是任何其他类型的数据都可以赋值给interface{}类型

Unmarshal字符串转结构体

json字符串转化成结构体的话,关键是json的字符串格式要严格符合规范,如:双引号需要反斜杠进行转义操作,括号要匹配不能缺失等。Unmarshal两个参数,一个是字节数组,另一个对空结构体取地址即可。

package main
 
import (
	"encoding/json"
	"fmt"
)
 
type Person struct {
	Name    interface{}
	Tall    interface{}
	Hobby   interface{}
	address interface{} //首字母小写
	Sex     interface{}
	Game    interface{}
}
 
func main() {
	data := "{\"Name\":\"Tony\",\"Tall\":173.5,\"Hobby\":\"Reading\",\"Sex\":\"男\",\"Game\":{\"Name1\":\"星际争霸\",\"Name2\":\"王者荣耀\"}}"
	s := []byte(data)
	p := Person{}
	err := json.Unmarshal(s, &p)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Printf("%T,%#v", p, p)
}
/*
main.Person,main.Person{Name:"Tony", Tall:173.5, Hobby:"Reading", address:interface {}(nil), Sex:"男", Game:map[string]interface {}{"Name1":"星际争霸", "Name2":"王者荣耀"}}
*/

我们通过反射看下它们各自的数据类型:

package main
 
import (
	"encoding/json"
	"fmt"
	"reflect"
)
 
type Person struct {
	Name    interface{}
	Tall    interface{}
	Hobby   interface{}
	address interface{} //首字母小写
	Sex     interface{}
	Game    interface{}
}
 
func main() {
	data := "{\"Name\":\"Tony\",\"Tall\":173.5,\"Hobby\":\"Reading\",\"Sex\":\"男\",\"Game\":{\"Name1\":\"星际争霸\",\"Name2\":\"王者荣耀\"}}"
	s := []byte(data)
	p := Person{}
 
	printType(&p)
	err := json.Unmarshal(s, &p)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("Unmarshal之后的结果")
	printType(&p)
}
func printType(p *Person) {
	nameType := reflect.TypeOf(p.Name)
	tallType := reflect.TypeOf(p.Tall)
	hobbyType := reflect.TypeOf(p.Hobby)
	addressType := reflect.TypeOf(p.address)
	sexType := reflect.TypeOf(p.Sex)
	gameType := reflect.TypeOf(p.Game)
 
	fmt.Println("nameType:", nameType)
	fmt.Println("ageType:", tallType)
	fmt.Println("highType:", hobbyType)
	fmt.Println("sexType:", addressType)
	fmt.Println("classType:", sexType)
	fmt.Println("testType:", gameType)
}
/*
nameType: <nil>
ageType: <nil>
highType: <nil>
sexType: <nil>
classType: <nil>
testType: <nil>
Unmarshal之后的结果
nameType: string
ageType: float64
highType: string
sexType: <nil>
classType: string
testType: map[string]interface {}
*/

最后还可以指定别名,大家可以试下:

Name    string `json:"name"`

这个就是用name代替Name

期待您的加入,畅所欲言的交流,一起成长为go语言技术达人

...全文
学知识交朋友
拼手气红包 10.00元
229 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

198

社区成员

发帖
与我相关
我的任务
社区描述
从零开始的小白到进阶成技术达人。 一起学习,一起进步。
golanggo1.19 个人社区 贵州省·贵阳市
社区管理员
  • 寅恪光潜
  • 网络豆
  • Somnus_小凯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

Go语言集各语言优势之大成,从基础到进阶

一起学习,一起进步,成为技术达人!

试试用AI创作助手写篇文章吧