zoukankan      html  css  js  c++  java
  • 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 string `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 
    ",resInterface)// []interface {}
    // 2、转成列表套interface格式的
    ret1 := resInterface.([]interface{})
    fmt.Printf("ret1>>> %T 
    ",ret1) // []interface {}
    // 3、里层的数据也需要转一下(结果只有一个所以没用循环)
    customerInterface := ret1[0].(map[string]interface{})
    fmt.Printf("type_customerDic>>> %T 
    ",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 
    ",id)// string
    fmt.Printf("name>>> %T 
    ",name)// string
    fmt.Printf("timeZone>>> %T 
    ",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 
    ", 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 
    ", id, id)
                    fmt.Printf("name: %T, %v 
    ", name, name)
                    fmt.Printf("manager: %T, %v 
    ", 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
    ",data)// {"level":["debug"],"message":["File Not Found","Stack OverFlowe"]}
        }
    
        // 生成便于阅读的格式
        if data, err := json.MarshalIndent(m,""," ");err == nil{
            fmt.Printf("%s
    ",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
    ",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 
    ",StuObj.Id)// int64
            fmt.Printf("%v 
    ",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
    ",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
    ",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 
    ",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 
    ",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 
    ",dbgInfos,dbgInfos)//main.DebugInfo   {{1 222} debug hahaha } 
        }else{
            fmt.Println(err)
        }
    }

    ~~~

  • 相关阅读:
    FORM中的get post方法区别
    二叉树优势
    Map 排序
    ajax+MultipartFile上传文件到本地
    阿里巴巴开发手册
    poi快速导入导出excel表格
    String.trim()、.contains()等的作用
    eclipse导入ssm框架的项目,报tomcat无法启动的错误,如下:
    lesson5_oa文件操作
    lesson4_函数
  • 原文地址:https://www.cnblogs.com/paulwhw/p/14068476.html
Copyright © 2011-2022 走看看