Golang序列化與反序列化操作總結及一些實踐經驗


前言

本文總結一下自己這一個多月寫Go代碼以來有關JSON序列化與反序列化的學習及實踐使用經驗,如有更好的包或者解決方法歡迎下方留言。

一些實踐經驗

將結構復雜的map數據直接解析為string處理 ***

實際中有個API返回的數據是這樣結構的:

{"id": "23846617xxxxx",

"name": "sisi-dpa-xxx",

"targeting": {
    "age_max": 34,
    "age_min": 25,
    "app_install_state": "not_installed",
    "excluded_custom_audiences": [
        {
            "id": "23843939736920230",
            "name": "install-7D"
        }
    ],
    "flexible_spec": [
        {
            "interests": [
                {
                    "id": "6002839660079",
                    "name": "化妝品"
                },
                {
                    "id": "6002991239659",
                    "name": "母子關系"
                },
                {
                    "id": "6003054884732",
                    "name": "抵用券"
                },
                {
                    "id": "6003088846792",
                    "name": "美容院"
                },
                {
                    "id": "6003103108917",
                    "name": "精品屋"
                },
                {
                    "id": "6003188355978",
                    "name": "連衣裙"
                },
                {
                    "id": "6003198476967",
                    "name": "手提包"
                },
                {
                    "id": "6003198851865",
                    "name": "約會"
                },
                {
                    "id": "6003220634758",
                    "name": "折扣商店"
                },
                {
                    "id": "6003255640088",
                    "name": "太陽鏡"
                },
                {
                    "id": "6003266225248",
                    "name": "珠寶"
                },
                {
                    "id": "6003346592981",
                    "name": "線上購物"
                },
                {
                    "id": "6003348453981",
                    "name": ""
                },
                {
                    "id": "6003351764757",
                    "name": "三項全能"
                },
                {
                    "id": "6003390752144",
                    "name": "購物廣場"
                },
                {
                    "id": "6003415393053",
                    "name": "童裝"
                },
                {
                    "id": "6003443805331",
                    "name": "香水"
                },
                {
                    "id": "6003445506042",
                    "name": "婚姻"
                },
                {
                    "id": "6003456330903",
                    "name": "美發產品"
                },
                {
                    "id": "6003476182657",
                    "name": "家人"
                },
                {
                    "id": "6004100985609",
                    "name": "友情"
                },
                {
                    "id": "6007828099136",
                    "name": "奢侈品"
                },
                {
                    "id": "6011366104268",
                    "name": "女裝"
                },
                {
                    "id": "6011994253127",
                    "name": "男裝"
                }
            ]
        }
    ],
    "genders": [
        2
    ],
    "geo_locations": {
        "countries": [
            "SA"
        ],
        "location_types": [
            "home",
            "recent"
        ]
    },
    "targeting_optimization": "expansion_all",
    "user_device": [
        "iPad",
        "iPhone",
        "iPod"
    ],
    "user_os": [
        "iOS"
    ],
    "brand_safety_content_filter_levels": [
        "FACEBOOK_STANDARD"
    ],
    "publisher_platforms": [
        "facebook",
        "instagram"
    ],
    "facebook_positions": [
        "feed",
        "video_feeds",
        "instant_article",
        "instream_video",
        "story",
        "search"
    ],
    "instagram_positions": [
        "stream",
        "story",
        "explore"
    ],
    "device_platforms": [
        "mobile"
    ]
    }
}
n多層的返回結構

可以看到,返回的字典中只有3個key,id、name與targeting,targeting里面的結構太復雜了,實際中我們是將這個復雜結構序列化為string存入數據庫的,定義結構體如下:

type AdSetFB struct {
    AdsetFbId string `json:"id" gorm:"-"`
    AdsetFbName string `json:"name" gorm:"-"`
    Targeting interface{} `json:"targeting" gorm:"-"`
}

在做數據處理的時候將復雜的targeting數據直接序列化為string:

// AdSet中的數據做處理
func (a *AdSetFB) Format() {
    // interface轉json
    if s, err := json.Marshal(a.Targeting); err == nil{
        a.Targeting = string(s)
    }else{
        a.Targeting = ""
    }
} 

返回的結構是一個列表格式的而不是字典格式string的處理 ***

實際中遇到一個這樣返回格式的數據:

[
    {
        "results": [
            {
                "customer": {
                    "resourceName": "customers/xxx",
                    "id": "xxx",
                    "descriptiveName": "hhh-自投-jjjs",
                    "timeZone": "Asia/Shanghai",
                    "manager": false,
                    "testAccount": false
                }
            }
        ],
        "fieldMask": "customer.timeZone,customer.id,customer.manager,customer.testAccount,customer.optimizationScore,customer.descriptiveName"
    }
]
直接返回一個列表格式的數據

直接解析為切片的解決方式

一開始還不太熟練JSON與go基礎類型的相互轉換,想着先將結果Unmarshal成一個列表,然后一步步的使用interface對象的轉型去處理:

// 先將結果轉成切片
var retInfo []map[string]interface{}
err1 := json.Unmarshal([]byte(strRet),&retInfo)

if err1 != nil{
     fmt.Println(err1.Error())
}
if len(retInfo) < 1{
    fmt.Println("沒返回數據")
}
// 1、獲取interface結果(結果只有一個所以沒用循環)
fmt.Println("results>>> ",retInfo[0]["results"])
resInterface := retInfo[0]["results"]
fmt.Printf("type_resInterface>>> %T \n",resInterface)// []interface {}
// 2、轉成列表套interface格式的
ret1 := resInterface.([]interface{})
fmt.Printf("ret1>>> %T \n",ret1) // []interface {}
// 3、里層的數據也需要轉一下(結果只有一個所以沒用循環)
customerInterface := ret1[0].(map[string]interface{})
fmt.Printf("type_customerDic>>> %T \n",customerInterface) // map[string]interface {}
// 4、獲取里層的customer字典
customerDic := customerInterface["customer"]
// 5、獲取customer字典的信息
// 注意這里的數據都是interface類型的,需要轉一下!轉成對應的格式!!!
id := customerDic.(map[string]interface{})["id"].(string)
name := customerDic.(map[string]interface{})["descriptiveName"].(string)
timeZone := customerDic.(map[string]interface{})["timeZone"].(string)
if name == nil{
    name = fmt.Sprintf("%s_noName",customerId)
}

fmt.Printf("id>>> %T \n",id)// string
fmt.Printf("name>>> %T \n",name)// string
fmt.Printf("timeZone>>> %T \n",timeZone)// string
直接解析為切片的解決方式

解析為切片套結構體的方法 ***

返回的格式是一個列表格式的數據。這樣格式的數據可以轉成切片嵌套結構體的結構去處理,下面是我實現的完整代碼:

package main

import (
    "encoding/json"
    "fmt"
)

// 返回的results結構
type ResultsResponse struct {
    Results   []*CustomerParent `json:"results"`
    FieldMask string            `json:"fieldMask"`
}

// 父級字典
type CustomerParent struct {
    // 兒子字典
    CustomerChildren *Customer `json:"customer"`
}

// 基礎結構
type Customer struct {
    Id       string `json:"id"`
    Name     string `json:"descriptiveName"`
    TimeZone string `json:"timeZone"`
    Manager  bool   `json:"manager"`
}

func main() {
    // 模擬返回的數據
    responseStr := `[
    {
        "results": [
            {
                "customer": {
                    "resourceName": "customers/xxx",
                    "id": "xxx",
                    "descriptiveName": "hhh-自投-jjja",
                    "timeZone": "Asia/Shanghai",
                    "manager": true,
                    "testAccount": false
                }
            }
        ],
        "fieldMask": "customer.timeZone,customer.id,customer.manager,customer.testAccount,customer.optimizationScore,customer.descriptiveName"
    }
]`
    // 將結果解析為 切片套結構體的形式
    var responseSlice []*ResultsResponse
    // 將字符串解析為 存放map的切片
    if err := json.Unmarshal([]byte(responseStr), &responseSlice); err == nil {
        fmt.Printf("rep: %v \n", responseSlice) //  rep: [0xc000090180]  切片中存的是地址
        // 從切片中獲取數據
        for _, retObj := range responseSlice {
            resultsLst := retObj.Results
            // 獲取resultsLst中的數據:先找父親再找兒子
            for _, customerParentObj := range resultsLst {
                customerObj := customerParentObj.CustomerChildren
                id := customerObj.Id
                name := customerObj.Name
                manager := customerObj.Manager
                fmt.Printf("id: %T, %v \n", id, id)
                fmt.Printf("name: %T, %v \n", name, name)
                fmt.Printf("manager: %T, %v \n", manager, manager)
                /*
                    id: string, xxx
                    name: string, hhh-自投-jjja
                    manager: bool, true
                */
            }
        }
    } else {
        fmt.Println("解析失敗!")
    }
}

遇到數字與string類型無需添加額外方法的轉換方案 ***

參考我的這篇博客:Golang結構體與JSON相互轉換時的小技巧

基礎:JSON的序列化

map轉JSON

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    // 定義一個map變量並初始化
    m := map[string][]string{
        "level": {"debug"},
        "message": {"File Not Found","Stack OverFlowe"},
    }
    // 將map解析為JSON格式
    if data, err := json.Marshal(m);err == nil{
        fmt.Printf("%s\n",data)// {"level":["debug"],"message":["File Not Found","Stack OverFlowe"]}
    }

    // 生成便於閱讀的格式
    if data, err := json.MarshalIndent(m,""," ");err == nil{
        fmt.Printf("%s\n",data)
        /*
        {
         "level": [
          "debug"
         ],
         "message": [
          "File Not Found",
          "Stack OverFlowe"
         ]
        }
        */
    }
}

結構體與JSON的互相轉換

結構體轉換成JSON在開發中經常會用到。

json包是通過反射機制來實現編解碼的,因此結構體必須導出所轉換的字段,沒有導出的字段不會被json包解析。

package main

import (
    "encoding/json"
    "fmt"
)

type Student struct{
  Id int64 `json:"id,string"`   // 注意這里的 ,string
    Name string `json:"name"`
    msg string // 小寫的不會被json解析
}

func main() {
    // 定義一個結構體切片初始化
    stucent := Student{"whw",123123,"666"}
    // 將結構體轉成json格式
    data, err := json.Marshal(stucent)
    if err == nil{
        // 注意這里將 Id轉換為了string
        fmt.Printf("%s\n",data)//{"name":"whw","id":"123123"}
    }

    // json反序列化為結構體 這里的id是 字符串類型的。。。
    s := `{"name":"whw","id":"123123"}`
    var StuObj Student
    if err := json.Unmarshal([]byte(s),&StuObj);err != nil{
        fmt.Println("err>>",err)
    }else{
        // 反序列化后 成了 int64 (,string 的作用)
        fmt.Printf("%T \n",StuObj.Id)// int64
        fmt.Printf("%v \n",StuObj)// {whw 123123 } 
    }
}

序列化時的結構體字段標簽

json包在解析結構體時,如果遇到key為JSON的字段標簽,則會按照一定規則解析該標簽:第一個出現的是字段在JSON串中使用的名字,之后為其他選項,例如omitempty指定空值字段不出現在JSON中。如果整個value為“-”,則不解析該字段。

package main

import (
    "encoding/json"
    "fmt"
)

type Student struct{
    Name string `json:"__name"`
    Id int64 `json:"id"`
    Age int `json:"-"` // 不解析該字段
    msg string // 小寫的不會被json解析
}

func main() {
    // 定義一個結構體切片初始化
    stucent := Student{"whw",123123,12,"阿斯頓發送到發"}
    // 將結構體轉成json格式
    data, err := json.Marshal(stucent)
    if err == nil{
        // 注意這里將 Id轉換為了string
        fmt.Printf("%s\n",data)//{"__name":"whw","id":123123}
    }
}

序列化時的匿名字段 

json包在解析匿名字段時,會將匿名字段的字段當成該結構體的字段處理:

package main

import (
    "encoding/json"
    "fmt"
)

type Point struct{
    X, Y int
}

type Student struct{
    Point
    Name string `json:"__name"`
    Id int64 `json:"id"`
    Age int `json:"-"` // 不解析該字段
    msg string // 小寫的不會被json解析
}

func main() {
    // 定義一個結構體切片初始化
    po := Point{1,2}
    stucent := Student{po,"whw",123123,12,"阿斯頓發送到發"}
    // 將結構體轉成json格式
    data, err := json.Marshal(stucent)
    if err == nil{
        // 注意這里將 Id轉換為了string
        fmt.Printf("%s\n",data)// {"X":1,"Y":2,"__name":"whw","id":123123}
    }
}

Marshal()注意事項

  • Marshal()函數只有在轉換成功的時候才會返回數據

  • JSON對象只支持string作為key,所以要編碼一個map,必須是map[string]T這種類型(T是Go語言中的任意類型)

  • channel、complex和function是不能被編碼成JSON的。

  • 指針在編碼的時候會輸出指針指向的內容,而空指針會輸出null。

基礎:JSON的返序列化(解析)

JSON轉切片

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    data := `[{"level":"debug","msg":"filexxx"},{"naem":"whw"}]`

    var dpInfos []map[string]string
    // 將字符串解析為map切片
    if err := json.Unmarshal([]byte(data),&dpInfos);err == nil{
        fmt.Println(dpInfos)// [map[level:debug msg:filexxx] map[naem:whw]]
    }
}

JSON轉結構體

JSON可以轉換成結構體。同編碼一樣,json包是通過反射機制來實現解碼的,因此結構體必須導出所轉換的字段,不導出的字段不會被json包解析。另外解析時不區分大小寫。

package main

import (
    "encoding/json"
    "fmt"
)

type DebugInfo struct {
    Level string
    Msg string
    author string // 首字母小寫不會被json解析
}

func (d *DebugInfo) JsonDump() string{
    return fmt.Sprintf("Level:%s,Msg:%s",d.Level,d.Msg)
}

func main() {
    // 定義JSON格式字符串
    data := `[{"level":"debug","msg":"hahaha"},` + `{"level":"error","msg":"hehehe"}]`
    var dbgInfos []DebugInfo
    // 轉成結構體切片
    if err := json.Unmarshal([]byte(data),&dbgInfos); err == nil{
        fmt.Printf("%T   %v \n",dbgInfos,dbgInfos)//[]main.DebugInfo   [{debug hahaha } {error hehehe }] 
    }
}

反序列化時結構體字段標簽

解碼時依然支持結構體字段標簽,規則和編碼時一樣: 

package main

import (
    "encoding/json"
    "fmt"
)

type DebugInfo struct {
    Level string `json:"level"`
    Msg string `json:"message"` // json里面的為message
    Author string `json:"-"` // 不會被解析
}

func (d *DebugInfo) JsonDump() string{
    return fmt.Sprintf("Level:%s,Msg:%s",d.Level,d.Msg)
}

func main() {
    // 定義JSON格式字符串
    data := `[{"level":"debug","message":"hahaha"},` + `{"level":"error","message":"hehehe"}]`
    var dbgInfos []DebugInfo
    // 轉成結構體切片
    if err := json.Unmarshal([]byte(data),&dbgInfos); err == nil{
        fmt.Printf("%T   %v \n",dbgInfos,dbgInfos)//[]main.DebugInfo   [{debug hahaha } {error hehehe }]
    }
}

反序列化時的匿名字段

package main

import (
    "encoding/json"
    "fmt"
)

type Point struct {
    X, Y int
}

type DebugInfo struct {
    Point
    Level string `json:"level"`
    Msg string `json:"message"` // json里面的為message
    Author string `json:"-"` // 不會被解析
}

func (d *DebugInfo) JsonDump() string{
    return fmt.Sprintf("Level:%s,Msg:%s",d.Level,d.Msg)
}

func main() {
    // 定義JSON格式字符串
    data := `{"level":"debug","message":"hahaha","X":1,"Y":222}`
    var dbgInfos DebugInfo
    // 轉成結構體切片
    if err := json.Unmarshal([]byte(data),&dbgInfos); err == nil{
        fmt.Printf("%T   %v \n",dbgInfos,dbgInfos)//main.DebugInfo   {{1 222} debug hahaha } 
    }else{
        fmt.Println(err)
    }
}

~~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM