go语言:json转换总结(json.Unmarshal、json.marshal)

本篇文章主要是本人在学习json与结构体转换过程中的一些摘抄笔记,总结在这里,复习使用。

Json(Javascript Object Nanotation)是一种数据交换格式,常用于前后端数据传输。任意一端将数据转换成json 字符串,另一端再将该字符串解析成相应的数据结构,如string类型,strcut对象等。

1. 下面是四种json转为结构体

  • 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
出生地:新疆乌鲁木齐市
作品:
         《阿娜尔罕》
         《逆光之恋》
         《克拉恋人》
  • 2. JSON内嵌普通JSON
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:《阿娜尔罕》
  • 3.JSON内嵌数组JSON
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 - 《克拉恋人》
  • 4.JSON内嵌具有动态Key的JSON
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

摘抄笔记如下:强烈建议去看原文,下面是我的一些摘抄,以便复习使用。

  • 1. 将数据编码成json字符串
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}}
  • 2. 空接口

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

  1. 对于不知道类型的多源数据流的解码通常这样:

    • 采用标准的encoding/json库将数据解码为通用map[string]interface{}类型
    • 利用mapstructure库转为相应的 Go 结构体
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处理字段映射是大小写不敏感的。

  1. 结构体的嵌套被认为是拥有该结构体名字的另一个字段
    • 首先看三种继承方式
// 方式一
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只有两个导出字段:

  • Keys:解码成功的键名;
  • Unused:在源数据中存在,但是目标结构中不存在的键名。
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。
  • 没有omitempty,会输出Docs字段。

深入理解转换

主要参考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)
}
  • 15
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Go语言中,判断结构体是否为空可以通过判断结构体的各个字段是否为空来实现。对于使用`json.Unmarshal`解析后的结构体,你可以通过判断其中的字段是否为其零值来确定结构体是否为空。以下是一个示例代码: ```go package main import ( "encoding/json" "fmt" ) type Person struct { Name string `json:"name"` Age int `json:"age"` Email string `json:"email"` } func IsEmptyStruct(s interface{}) bool { // 将结构体转换JSON字符串 jsonStr, _ := json.Marshal(s) // 判断JSON字符串是否为空 return string(jsonStr) == "{}" } func main() { // 示例1:空结构体 var p1 Person fmt.Println("Is p1 empty?", IsEmptyStruct(p1)) // 示例2:非空结构体 p2 := Person{Name: "Alice", Age: 30, Email: "alice@example.com"} fmt.Println("Is p2 empty?", IsEmptyStruct(p2)) } ``` 在上述代码中,我们定义了一个`Person`结构体,并实现了一个`IsEmptyStruct`函数来判断结构体是否为空。函数内部先将结构体转换JSON字符串,然后判断该JSON字符串是否为空。如果JSON字符串为空字符串`{}`,则说明结构体为空;否则,结构体不为空。 在示例中,我们分别创建了一个空结构体`p1`和一个非空结构体`p2`,并通过调用`IsEmptyStruct`函数来判断它们是否为空。 请注意,这种方法只能判断结构体中的字段是否为空,不能判断结构体中的嵌套结构体是否为空。如果需要判断嵌套结构体的空值,可以递归地对嵌套结构体进行判断。同时,结构体的零值和空值的定义可能因字段类型的不同而不同,需要根据实际情况进行判断。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值