本篇文章主要是本人在学习json与结构体转换过程中的一些摘抄笔记,总结在这里,复习使用。
Json(Javascript Object Nanotation)是一种数据交换格式,常用于前后端数据传输。任意一端将数据转换成json 字符串,另一端再将该字符串解析成相应的数据结构,如string类型,strcut对象等。
1. 下面是四种json转为结构体
package main
import (
"encoding/json"
"fmt"
)
// Actress 女演员
type Actress struct {
Name string
Birthday string
BirthPlace string
Opus []string
}
func main() {
// 普通JSON
// 因为json.UnMarshal() 函数接收的参数是字节切片
// 所以需要把JSON字符串转换成字节切片。
jsonData := []byte(`{
"name":"迪丽热巴",
"birthday":"1992-06-03",
"birthPlace":"新疆乌鲁木齐市",
"opus":[
"《阿娜尔罕》",
"《逆光之恋》",
"《克拉恋人》"
]
}`)
var actress Actress
err := json.Unmarshal(jsonData, &actress)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Printf("姓名:%s\n", actress.Name)
fmt.Printf("生日:%s\n", actress.Birthday)
fmt.Printf("出生地:%s\n", actress.BirthPlace)
fmt.Println("作品:")
for _, val := range actress.Opus {
fmt.Println("\t", val)
}
}
结果
姓名:迪丽热巴
生日:1992-06-03
出生地:新疆乌鲁木齐市
作品:
《阿娜尔罕》
《逆光之恋》
《克拉恋人》
package main
import (
"encoding/json"
"fmt"
)
// Opus 作品
type Opus struct {
Date string
Title string
}
// Actress 女演员
type Actress struct {
Name string
Birthday string
BirthPlace string
Opus Opus
}
func main () {
// JSON嵌套普通JSON
jsonData := []byte(`{
"name":"迪丽热巴",
"birthday":"1992-06-03",
"birthPlace":"新疆乌鲁木齐市",
"opus": {
"Date":"2013",
"Title":"《阿娜尔罕》"
}
}`)
var actress Actress
err := json.Unmarshal(jsonData, &actress)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Printf("姓名:%s\n", actress.Name)
fmt.Printf("生日:%s\n", actress.Birthday)
fmt.Printf("出生地:%s\n", actress.BirthPlace)
fmt.Println("作品:")
fmt.Printf("\t%s:%s", actress.Opus.Date, actress.Opus.Title)}
结果
姓名:迪丽热巴
生日:1992-06-03
出生地:新疆乌鲁木齐市
作品:
2013:《阿娜尔罕》
package main
import (
"encoding/json"
"fmt"
)
type Opus struct {
Date string
Title string
}
type Actress struct {
Name string
Birthday string
BirthPlace string
Opus []Opus
}
func main () {
// JSON嵌套数组JSON
jsonData := []byte(`{
"name":"迪丽热巴",
"birthday":"1992-06-03",
"birthPlace":"新疆乌鲁木齐市",
"opus":[
{
"date":"2013",
"title":"《阿娜尔罕》"
},
{
"date":"2014",
"title":"《逆光之恋》"
},
{
"date":"2015",
"title":"《克拉恋人》"
}
]
}`)
var actress Actress
err := json.Unmarshal(jsonData, &actress)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Printf("姓名:%s\n", actress.Name)
fmt.Printf("生日:%s\n", actress.Birthday)
fmt.Printf("出生地:%s\n", actress.BirthPlace)
fmt.Println("作品:")
for _, val := range actress.Opus {
fmt.Printf("\t%s - %s\n", val.Date, val.Title)
}
}
结果
姓名:迪丽热巴
生日:1992-06-03
出生地:新疆乌鲁木齐市
作品:
2013 - 《阿娜尔罕》
2014 - 《逆光之恋》
2015 - 《克拉恋人》
package main
import (
"encoding/json"
"fmt"
)
// Opus 作品
type Opus struct {
Type string
Title string
}
// Actress 女演员
type Actress struct {
Name string
Birthday string
BirthPlace string
Opus map[string]Opus
}
func main () {
jsonData := []byte(`{
"name":"迪丽热巴",
"birthday":"1992-06-03",
"birthPlace":"新疆乌鲁木齐市",
"opus":{
"2013":{
"Type":"近代革命剧",
"Title":"《阿娜尔罕》"
},
"2014":{
"Type":"奇幻剧",
"Title":"《逆光之恋》"
},
"2015":{
"Type":"爱情剧",
"Title":"《克拉恋人》"
}
}
}`)
var actress Actress
err := json.Unmarshal(jsonData, &actress)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Printf("姓名:%s\n", actress.Name)
fmt.Printf("生日:%s\n", actress.Birthday)
fmt.Printf("出生地:%s\n", actress.BirthPlace)
fmt.Println("作品:")
for index, value := range actress.Opus {
fmt.Printf("\t日期:%s\n", index)
fmt.Printf("\t\t分类:%s\n", value.Type)
fmt.Printf("\t\t标题:%s\n", value.Title)
}
}
结果
姓名:迪丽热巴
生日:1992-06-03
出生地:新疆乌鲁木齐市
作品:
日期:2013
分类:近代革命剧
标题:《阿娜尔罕》
日期:2014
分类:奇幻剧
标题:《逆光之恋》
日期:2015
分类:爱情剧
标题:《克拉恋人》
转载自:Golang语言之JSON解码函数Unmarshal
2. 另一篇很好的文章是:Go的json解析:Marshal与Unmarshal
摘抄笔记如下:强烈建议去看原文,下面是我的一些摘抄,以便复习使用。
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)
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))
}
结果是小写的key不能被json化,因为首字母小写的为私有属性,大写的为公有属性。
{"name":"张三","Age":18,"HIgh":true,"class":{"Name":"1班","Grade":3}}
interface{} 类型其实是个空接口,即没有方法的接口。go的每一种类型都实现了该接口。因此,任何其他类型的数据都可以赋值给interface{}类型。
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() {
//与前面的例子一样
......
}
3. 还有这个:Go 每日一库之 mapstructure
对于不知道类型的多源数据流的解码通常这样:
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/mitchellh/mapstructure"
)
type Person struct {
Name string
Age int
Job string
}
type Cat struct {
Name string
Age int
Breed string
}
func (c *Cat) miao(){
fmt.Println("喵喵喵~")
}
func main() {
datas := []string{
`{
"type": "person",
"name":"dj",
"age":18,
"job": "programmer"
}`,
`{
"type": "cat",
"name": "kitty",
"age": 1,
"breed": "Ragdoll"
}`,
}
for _, data := range datas {
var m map[string]interface{}
err := json.Unmarshal([]byte(data), &m) //转到通用接口中
if err != nil {
log.Fatal(err)
}
fmt.Println(m["type"])
//输出 person
fmt.Println(m)
//输出 map[age:18 job:programmer name:dj type:person]
switch m["type"].(string) { //获取key-value,接口类型转为字符出(断言)
case "person":
var p Person
mapstructure.Decode(m, &p) //转为结构体
fmt.Println("person", p)
//输出 person {dj 18 programmer}
case "cat":
var cat Cat
mapstructure.Decode(m, &cat)
fmt.Println("cat", cat)
cat.miao()
}
}
}
结果
省略...
map[age:1 breed:Ragdoll name:kitty type:cat]
cat {kitty 1 Ragdoll}
喵喵喵~
上面的json中首字母大小写都有,所以mapstructure处理字段映射是大小写不敏感的。
// 方式一
type Friend struct {
Person
}
// 方式二
type Friend struct {
Person Person
}
// 将结构体字段提到父结构中
type Friend struct {
Person `mapstructure:",squash"`
}
例子
package main
import (
"encoding/json"
"fmt"
"github.com/mitchellh/mapstructure"
"log"
)
type Person struct {
Name string
}
type Friend1 struct {
Person
}
type Friend2 struct {
Person `mapstructure:",squash"`
}
func main() {
datas := []string{`
{
"type": "friend1",
"person": {
"name":"dj"
}
}`,
`{
"type": "friend2",
"name": "dj2"
}`,
}
for _, data := range datas {
var m map[string]interface{}
err := json.Unmarshal([]byte(data), &m)
if err != nil {
log.Fatal(err)
}
switch m["type"].(string) {
case "friend1":
var f1 Friend1
mapstructure.Decode(m, &f1)
fmt.Println("friend1", f1)
case "friend2":
var f2 Friend2
mapstructure.Decode(m, &f2)
fmt.Println("friend2", f2)
}
}
}
结果
friend1 {{dj}}
friend2 {{dj2}}
结构体中无对应的字段,mapstructure会忽略它。在结构体中设置mapstructure:",remain"标签。未映射的值就会添加到这个字段中。注意,这个字段的类型只能为map[string]interface{}或map[interface{}]interface{}。
package main
import (
"encoding/json"
"fmt"
"github.com/mitchellh/mapstructure"
"log"
)
type Person struct {
Name string
Age int
Job string
Other map[string]interface{} `mapstructure:",remain"`
}
func main() {
data := `
{
"name": "dj",
"age":18,
"job":"programmer",
"height":"1.8m",
"handsome": true
}`
var m map[string]interface{}
err := json.Unmarshal([]byte(data), &m)
if err != nil {
log.Fatal(err)
}
var p Person
mapstructure.Decode(m, &p)
fmt.Println("other", p.Other)
}
输出
other map[handsome:true height:1.8m]
逆向转换
将 结构体反向解码为map[string]interface{}。在反向解码时,我们可以为某些字段设置mapstructure:",omitempty"。这样当这些字段为默认值时,就不会出现在结构的map[string]interface{}中:
package main
import (
"encoding/json"
"fmt"
"github.com/mitchellh/mapstructure"
)
type Person struct {
Name string
Age int
Job string `mapstructure:",omitempty"`
}
func main() {
p := &Person{
Name: "dj",
Age: 18,
Job: "student",
}
fmt.Println("p:", *p)
var m map[string]interface{}
mapstructure.Decode(p, &m)
data, _ := json.Marshal(m)
fmt.Println(string(data))
}
输出
p: {dj 18 student}
{"":"student","Age":18,"Name":"dj"}
Metadata
mapstructure可以使用Metadata收集这些信息
Metadata只有两个导出字段:
package main
import (
"fmt"
"github.com/mitchellh/mapstructure"
)
type Person struct {
Name string
Age int
}
func main() {
m := map[string]interface{}{
"name": "dj",
"age": 18,
"job": "programmer",
}
var p Person
var metadata mapstructure.Metadata
mapstructure.DecodeMetadata(m, &p, &metadata)
fmt.Printf("keys:%#v unused:%#v\n", metadata.Keys, metadata.Unused)
}
结果
keys:[]string{"Name", "Age"} unused:[]string{"job"}
错误处理
未转载...
弱类型输入
未转载...
4. 学习参考 GO 全面解析 json tag 篇
golang中命名用驼峰,小写包外无法引用,为了在结构体转json(存储等)时得到想要的格式,通常采用tag的方式声明。
package main
import (
"encoding/json"
"fmt"
)
type User struct {
UserId int //`json:"user_id" bson:"user_id"`
UserName string `json:"user_name" bson:"user_name"`
}
func main(){
u := &User{UserId: 123, UserName: "along"}
j, err := json.Marshal(u)
if err != nil {
fmt.Println("Marshal error!")
}
fmt.Println(string(j))
fmt.Println()
}
输出,注意只有一个变成了小写下划线
{"UserId":123,"user_name":"along"}
使用反射包(reflect)中的方法来获取tag中的内容
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type User struct {
UserId int `json:"user_id" bson:"user_id"`
UserName string `json:"user_name" bson:"user_name"`
}
func main() {
// 输出json格式
u := &User{UserId: 1, UserName: "tony"}
j, _ := json.Marshal(u)
fmt.Println(string(j))
// 输出内容:{"user_id":1,"user_name":"tony"}
// 获取tag中的内容
t := reflect.TypeOf(u)
field := t.Elem().Field(0)
fmt.Println(field.Tag.Get("json"))
// 输出:user_id
fmt.Println(field.Tag.Get("bson"))
// 输出:user_id
}
5 . 学习的第五篇: golang中json的omitempty使用
json:"docs,omitempty"
用法
package main
import (
"encoding/json"
"fmt"
)
type Project struct {
Name string `json:"name"`
Url string `json:"url"`
Docs string `json:"docs,omitempty"` //有就有,没有就拉到
}
func main() {
p1 := Project{
Name:"hello name",
Url:"https://www.baidu.com/",
}
data, err := json.Marshal(p1)
if err != nil {
panic(err)
}
// Docs定义为omitempty所以不会出现Docs的字段
fmt.Printf("%s\n", data)
p2 := Project{
Name:"baidu",
Url:"https://www.baidu.com/",
Docs:"https://www.baidu.com/",
}
data2, err := json.Marshal(p2)
if err != nil {
panic(err)
}
//打印出所有的字段
fmt.Printf("%s\n", data2)
}
结果
{"name":"hello name","url":"https://www.baidu.com/"}
{"name":"baidu","url":"https://www.baidu.com/","docs":"https://www.baidu.com/"}
对比
package main
import (
"encoding/json"
"fmt"
)
type Project struct {
Name string `json:"name"`
Url string `json:"url"`
Docs string //`json:"docs,omitempty"` //进行对比
}
func main() {
p1 := Project{
Name:"hello name",
Url:"https://www.baidu.com/",
}
data, err := json.Marshal(p1)
if err != nil {
panic(err)
}
// Docs定义为omitempty所以不会出现Docs的字段
fmt.Printf("%s\n", data)
p2 := Project{
Name:"baidu",
Url:"https://www.baidu.com/",
Docs:"https://www.baidu.com/",
}
data2, err := json.Marshal(p2)
if err != nil {
panic(err)
}
//打印出所有的字段
fmt.Printf("%s\n", data2)
}
输出
{"name":"hello name","url":"https://www.baidu.com/","Docs":""}
{"name":"baidu","url":"https://www.baidu.com/","Docs":"https://www.baidu.com/"}
上面有两处不同:
json:"docs"
,会按照结构体里的名字进行转json。深入理解转换
主要参考Golang 的 “omitempty” 关键字略解中的陷阱章节中主要讲:对于用 omitempty 定义的 field ,如果给它赋的值恰好等于默认空值的话,在转为 json 之后也不会输出这个 field 。
思考一个小例子
如果json少的字段,转为结构体时,结构体中有该字段,转换之后依然有。
如果json多出来的字段,转为结构体时,结构体中没有,转换之后依然没有。
这两句话就像是,在给结构体赋值,有的就赋值,没有就空着,omitempty这样写的json:"docs,omitempty"
,是转json时候的行为,转为docs时,Docs转为docs。
package main
import (
"encoding/json"
"fmt"
)
type Project struct {
Name string `json:"name"`
Url string `json:"url"`
Docs string `json:"docs,omitempty"` //
}
func main() {
//json 转结构体,为空的字段照样有
json1 := []byte(`{"url":"https://blog.csdn.net/qq_30505673","name":"hello name","name2":"hello name"}`)
var project Project
err = json.Unmarshal(json1, &project)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(project.Name)
fmt.Println(project.Url)
fmt.Println("?")
fmt.Println(project.Docs)
project.Docs = "along" //可以赋值
fmt.Println(project.Docs)
}
输出
hello name
https://blog.csdn.net/qq_30505673
?
along
整型不能给字符串
package main
import (
"encoding/json"
"fmt"
)
type Project struct {
Name string `json:"name"`
Url string `json:"url"`
Docs string `json:"docs,omitempty"` //
Num int `json:"num"`
}
func main() {
//json 转结构体,为空的字段照样有
json1 := []byte(`{"url":"https://blog.csdn.net/qq_30505673","name":"hello name","name2":"hello name","num":2}`)
var project Project
err := json.Unmarshal(json1, &project)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(project.Name)
fmt.Println(project.Url)
fmt.Println("?")
fmt.Println(project.Docs)
project.Docs = "along" //可以赋值
fmt.Println(project.Docs)
}