首頁 > 軟體

Go如何實現json字串與各類struct相互轉換

2022-08-30 18:03:40

json字串與各類struct相互轉換

不廢話了都在程式碼中了

 
package main 
import (
   "fmt"
   "reflect"
   "encoding/json"
   "strings"
)
 
type Class struct {
   Grade int `json:"grade"` //年級
   ClassNumber int  `json:"classNumber"` //班級號
}
 
type Student struct{
   Name string  //大寫開頭,可被匯出,沒有`json:".."`,匯出json的欄位名是原本名稱
   age int  //小寫開題,不可被匯出
   Hight int `json:"currentHight"` //匯出對應json的欄位名為currentHight
   Class *Class `class` //指標,指向參照物件;如果不用指標,只是值複製
}
 
func doMarshal(){//物件轉json字串
   nClass:=new(Class)//new只給給特定型別分配記憶體,設定「零」值,返回其地址(指標)
   fmt.Printf("nClass的型別是%s,內容是%vn",reflect.TypeOf(nClass),*nClass)
   nClass.Grade=3
   nClass.ClassNumber=6
   nStudents:=make([]*Student,0)
   //make只用於map,slice和channel,並且不顯示返回指標
   //這個切片,存放Student的指標
   nStudent:=Student{"Lily",7,116,nClass}
   jsonBytes,err1:=json.Marshal(nStudent)//解析後的是[]byte
   if err1!=nil{
      fmt.Printf("轉json失敗:%vn",err1)
      return
   }
   fmt.Println("轉成的JSON:") //age不會被匯出
   //{"Name":"Lily","currentHight":116,"Class":{"grade":3,"classNumber":6}}
   fmt.Println(string(jsonBytes))
   nStudents=append(nStudents,&Student{"Lilei",8,130,nClass})
   nStudents=append(nStudents,&nStudent)
   josnListBytes,err2:=json.Marshal(nStudents)
   if err2!=nil{
      fmt.Printf("轉jsonList失敗:%vn",err2)
      return
   }
   fmt.Println("轉成的列表型JSON:")
   fmt.Println(string(josnListBytes))
   //[{"Name":"Lilei","currentHight":130,"Class":{"grade":3,"classNumber":6}},{"Name":"Lily","currentHight":116,"Class":{"grade":3,"classNumber":6}}]
}
 
func doUnMarshal(){//json字串轉物件
   jsonStr:=`
      {
         "Name":"Lily",
         "currentHight":116,
         "age":12,
         "Class":{
            "grade":3,
            "classNumber":6
         },
         "score":[98,100,95]
      }
   `
   jsonListStr:=`[
      {
         "Name":"Lucy",
         "currentHight":120,
         "Class":{
            "grade":3,
            "classNumber":5
         }
      },
      {
         "Name":"Lily",
         "currentHight":116,
         "Class":{
            "grade":3,
            "classNumber":6
         }
      }
   ]`
   //第一種解析json方式,解析到Struct/[]Struct
   student:=Student{}//同new(Student)
   err:=json.Unmarshal([]byte(jsonStr),&student)
   //Unmarshall第2個引數必須是指標,否則報錯:json: Unmarshal(non-pointer main.Student)
   //因為必須解析到具體的物件,所以需傳入物件參照,而不是值傳遞
   //score在Student中沒有此欄位,所以被忽略了
   if err!=nil{
      fmt.Printf("解析json字串異常:%sn",err)
   }
   fmt.Printf("學生的名稱是%v,班級資訊是%v,年齡是%v(私有物件不能匯入,初始為0)n",student.Name,*student.Class,student.age)
   //學生的名稱是Lily,學生的班級資訊是{3 6},學生的年齡是0
   students:=[]*Student{} //定義切片,同make([]*Student,0)
   err=json.Unmarshal([]byte(jsonListStr),&students)
   if err!=nil{
      fmt.Printf("解析json字串異常:%sn",err)
   }
   for _,stu:=range students{ //這裡stu是指標型別,獲取其屬性可以直接用.Name,也可以解除參照後用.Name
      fmt.Printf("列表:學生的名稱是%s,身高是%d,在%d年級%d班n",stu.Name,(*stu).Hight,(*stu.Class).Grade,stu.Class.ClassNumber)
   }
   //第二種解析到interface{}/[]interface{}
   fmt.Println("*************解析json*************")
   var student1 interface{}
   err=json.Unmarshal([]byte(jsonStr),&student1)
   if err!=nil{
      fmt.Printf("解析json字串異常:%sn",err)
   }
   c:=-1
   resolve2JosnObj(student1,c)
   /*
   *************解析json*************
   map元素:
   map[Name]的元素: 型別是string,值是 Lily
   map[currentHight]的元素: 型別float64,值是 116
   map[age]的元素: 型別float64,值是 12
   map[Class]的元素: map元素:
   ---map[classNumber]的元素: 型別float64,值是 6
   ---map[grade]的元素: 型別float64,值是 3
   map[score]的元素: list元素:
   ---第0個元素: 型別float64,值是 98
   ---第1個元素: 型別float64,值是 100
   ---第2個元素: 型別float64,值是 95
    */
   fmt.Println("*************解析jsonlist*************")
   var students1 interface{}
   err=json.Unmarshal([]byte(jsonListStr),&students1)
   if err!=nil{
      fmt.Printf("解析jsonlist字串異常:%sn",err)
   }
   d:=-1
   resolve2JosnObj(students1,d)
   /*
   *************解析jsonlist*************
   list元素:
   第0個元素: map元素:
   ---map[Name]的元素: 型別是string,值是 Lucy
   ---map[currentHight]的元素: 型別float64,值是 120
   ---map[Class]的元素: map元素:
   ------map[grade]的元素: 型別float64,值是 3
   ------map[classNumber]的元素: 型別float64,值是 5
   第1個元素: map元素:
   ---map[Class]的元素: map元素:
   ------map[grade]的元素: 型別float64,值是 3
   ------map[classNumber]的元素: 型別float64,值是 6
   ---map[Name]的元素: 型別是string,值是 Lily
   ---map[currentHight]的元素: 型別float64,值是 116
    */
}
 
func resolve2JosnObj(objI interface{},c int){
   c=c+1
   switch obj:=objI.(type) { //此處[interface{}].(type) 專門用於switch的型別判斷
   case string:
      fmt.Println("型別是string,值是",obj)
   case float64:
      fmt.Println("型別float64,值是",obj)
   case map[string]interface{}:
      fmt.Println("map元素:")
      for k,vi:=range obj{
         fmt.Printf("%smap[%s]的元素: ",strings.Repeat("---",c),k)
         resolve2JosnObj(vi,c)
      }
   case []interface{}:
      fmt.Println("list元素:")
      for i,vi:=range obj{
         fmt.Printf("%s第%d個元素: ",strings.Repeat("---",c),i)
         resolve2JosnObj(vi,c)
      }
 
   default:
      fmt.Println("無法判斷型別,型別是",reflect.TypeOf(obj),"值是",obj) 
   } 
}
 
func main() {
   doMarshal()//物件轉json字串
   doUnMarshal()
}

簡單總結

1、結構體物件可生成json字串,Marshal()是[]byte,需要string去轉換

2、json字串可以對映到一個struct,但僅限公共元素(大寫開頭);也可通用的轉換到空介面interfece[],使用對應轉換到需要的內容

結構體轉換為JSON字串的一個坑

通過json.Marshal來將結構體資料轉換為json字串時,需要注意結構體內成員變數的首字母大小寫的問題,很容易會掉進坑裡.

來看一下這個例子

package main
import (
	"encoding/json"
	"fmt"
)
type Student struct {
	Name string
	age int
}
func main() {
	var s Student = Student {
		Name: "xiaomo",
		age: 18,
	}
	fmt.Printf("%+vn", s)
	res, _ := json.Marshal(s)
	fmt.Println(string(res))
}

執行輸出如下:

$ go run test_json.go
{Name:xiaomo age:18}
{"Name":"xiaomo"}

可以看到轉換的json字串中只包含了Name欄位,age欄位被忽略了.這是由於在進行json解析時,只會轉換結構體能夠匯出的欄位(首字母大寫),其他欄位將會被忽略.

這個機制也有個好處,可以根據實際需要,將想要匯出欄位的名字首字母大寫,其他欄位首字母小寫隱藏起來即可.

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com