網(wǎng)站首頁 編程語言 正文
在數(shù)據(jù)傳遞時,需要先編解碼;常用的方式是JSON編解碼(參見《golang之JSON處理》)。但有時卻需要讀取部分字段后,才能知道具體類型,此時就可借助mapstructure庫了。
mapstructure庫
mapstructure可方便地實現(xiàn)map[string]interface{}
與struct
間的轉換;使用前,需要先導入庫:
go get github.com/mitchellh/mapstructure
字段標簽
默認情況下,mapstructure使用字段的名稱做匹配映射(即在map中以字段名為鍵值查找字段值);注意匹配時是忽略大小寫的。也可通過標簽來設定字段映射名稱:
type Person struct { Name string `mapstructure:"userName"` }
內(nèi)嵌結構
go中結構體是可以任意嵌套的;嵌套后即認為擁有對應的字段。但是,默認情況下mapstructure只處理當前結構定義的字段,若要自動處理內(nèi)嵌字段需要添加標簽squash
:
type Student struct { Person `mapstructure:",squash"` Age int }
未映射字段
若源數(shù)據(jù)中有未映射的值(即結構體中無對應的字段),mapstructure默認會忽略它。可以在結構體中定義一個特殊字段(類型為map[string]interface{}
,且標簽要設置為mapstructure:",remain"
),來存放所有未能映射的字段中。
type Student struct { Name string Age int Other map[string]interface{} `mapstructure:",remain"` }
Metadata
mapstructure中可以使用Metadata收集一些解碼時會產(chǎn)生的有用信息。
// mapstructure.go type Metadata struct { Keys []string // 解碼成功的鍵 Unused []string // 源數(shù)據(jù)中存在,但目標結構中不存在的鍵 Unset []string // 未設定的(源數(shù)據(jù)中缺失的)鍵 }
為了獲取這些信息,需要使用DecodeMetadata來解碼:
? var metadata mapstructure.Metadata
? err := mapstructure.DecodeMetadata(m, &p, &metadata)
弱類型輸入
有時候,并不想對結構體字段類型和map[string]interface{}
的對應鍵值做強類型一致的校驗。這時可以使用WeakDecode/WeakDecodeMetadata方法,它們會嘗試做類型轉換:
- 布爾轉字符串:true = “1”, false = “0”;
- 布爾轉數(shù)字:true = 1, false = 0;
- 數(shù)字轉布爾:true if value != 0;
- 字符串轉布爾:可接受,
- 真:1, t, T, TRUE, true, True
- 假:0, f, F, FALSE, false, False
- 數(shù)字轉字符串:自動base10轉換;
- 負數(shù)轉為無符號數(shù)(上溢);
- 字符串轉數(shù)字:根據(jù)前綴(如0x等)轉換;
- 空數(shù)組與空map間互轉;
- 單個值轉為切片;
逆向轉換
除將map轉換為結構體外,mapstructure也可以將結構體反向解碼為map[string]interface{}
。在反向解碼時,我們可以為某些字段設置mapstructure:“,omitempty”,當這些字段為默認值時,就不會出現(xiàn)在map中:
p := &Student{ Name: "Mike", Age: 12, } var m map[string]interface{} mapstructure.Decode(p, &m)
解碼器
mapstructure提供了解碼器(Decoder),可靈活方便地控制解碼:
type DecoderConfig struct { // 若設定,則在任何解碼或類型轉換(設定了WeaklyTypedInput)前調用;對于設定了squash的內(nèi)嵌字段,整體調用一次;若返回錯誤,則整個解碼失敗 DecodeHook DecodeHookFunc // 若設定,則源數(shù)據(jù)中存在未使用字段時,報錯 ErrorUnused bool // 若設定,則有字段未設定時,報錯 ErrorUnset bool // 若設定,則在設定字段前先清空(對于map等類型會先清理掉舊數(shù)據(jù)) ZeroFields bool // 若設定,支持若類型間的轉換 WeaklyTypedInput bool // Squash will squash embedded structs. Squash bool // Metadata is the struct that will contain extra metadata about // the decoding. If this is nil, then no metadata will be tracked. Metadata *Metadata // Result is a pointer to the struct that will contain the decoded // value. Result interface{} // The tag name that mapstructure reads for field names. This // defaults to "mapstructure" TagName string // IgnoreUntaggedFields ignores all struct fields without explicit // TagName, comparable to `mapstructure:"-"` as default behaviour. IgnoreUntaggedFields bool // MatchName is the function used to match the map key to the struct // field name or tag. Defaults to `strings.EqualFold`. This can be used // to implement case-sensitive tag values, support snake casing, etc. MatchName func(mapKey, fieldName string) bool }
一個支持弱類型轉換的示例:要獲取的結果放到config的result中
Name string Age int } func decoderConfig() { m := map[string]interface{}{ "name": 123, "age": "12", "job": "programmer", } var p Person var metadata mapstructure.Metadata decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ WeaklyTypedInput: true, Result: &p, Metadata: &metadata, }) if err != nil { log.Fatal(err) } err = decoder.Decode(m) if err == nil { log.Printf("Result: %#v", p) log.Printf("keys:%#v, unused:%#v\n", metadata.Keys, metadata.Unused) } else { log.Println("decode fail:", err) } }
示例
通過一個messageData結構,action會指示最終的data類型。接收到數(shù)據(jù)后,先解析出atcion,再根據(jù)action轉換為真實的類型。
因time.Time是一個結構體(json序列化時會轉換為時間字符串),mapstructure無法正確處理,所以推薦使用時間戳。
為了能正確解析內(nèi)嵌的DataBasic,需要標記為squash。
import "github.com/mitchellh/mapstructure" type DataBasic struct { DataId string `json:"dataId"` UpdateTime int64 `json:"updateTime"` } type AddedData struct { DataBasic `mapstructure:",squash"` Tag string `json:"tag"` AddParams map[string]any `json:"addParams"` } type messageData struct { Action int `json:"action"` SeqId uint64 `json:"seqId"` Data any `json:"data"` } func decodeData() { add := &AddedData{ DataBasic: DataBasic{ DataId: "a2", UpdateTime: time.Now().UnixMilli(), }, Tag: "tag", AddParams: map[string]any{"dataId": "c2", "otherId": "t2"}, } data := &messageData{ Action: 1, Data: add, } js, err := json.Marshal(data) if err != nil { log.Printf("marshal fail: %v", err) return } got := &messageData{} err = json.Unmarshal(js, got) if err != nil { log.Printf("unmarshal fail: %v", err) return } param := new(AddedData) err = mapstructure.Decode(got.Data, param) if err != nil { log.Printf("unmarshal fail: %v", err) return } log.Printf("param: %+v", param) }
原文鏈接:https://blog.csdn.net/alwaysrun/article/details/128516027
相關推薦
- 2022-11-25 ASP.NET?MVC使用異步Action的方法_實用技巧
- 2022-12-28 Python?PyQt5中窗口數(shù)據(jù)傳遞的示例詳解_python
- 2024-01-06 RocketMQ死信消息解決方案
- 2022-07-06 C語言for循環(huán)嵌套for循環(huán)在實踐題目中應用詳解_C 語言
- 2022-06-30 利用Python刪除電腦中重復文件的方法_python
- 2022-08-25 C/C++內(nèi)存管理基礎與面試_C 語言
- 2022-04-04 webpack-loader: loader的使用(圖片、txt文件、url、less)
- 2023-01-15 PyQt5+QtChart實現(xiàn)繪制極坐標圖_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結構-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支