網(wǎng)站首頁 編程語言 正文
用法
熟悉 Golang 的朋友對于 json 和 struct 之間的轉(zhuǎn)換一定不陌生,為了將代碼中的結(jié)構(gòu)體與 json 數(shù)據(jù)解耦,通常我們會在結(jié)構(gòu)體的 field 類型后加上解釋說明,例如在表示一個地址的時候, json 數(shù)據(jù)如下所示
{ "street": "200 Larkin St", "city": "San Francisco", "state": "CA", "zipcode": "94102" }
與之相對應的 Golang 結(jié)構(gòu)體表示可能是這個樣子的
type address struct { Street string `json:"street"` // 街道 Ste string `json:"suite"` // 單元(可以不存在) City string `json:"city"` // 城市 State string `json:"state"` // 州/省 Zipcode string `json:"zipcode"` // 郵編 }
這樣無論代碼中的變量如何改變,我們都能成功將 json 數(shù)據(jù)解析出來,獲得正確的街道,城市等信息,到目前為止一切正常。但如果我們想要將地址結(jié)構(gòu)體恢復成 json 格式時,問題就來了。比方說我們用下面這段代碼讀取了地址 json ,然后根據(jù)業(yè)務邏輯處理了之后恢復成正常的 json 打印出來
func main() { ? ? ? ? data := `{ ? ? ? ? "street": "200 Larkin St", ? ? ? ? "city": "San Francisco", ? ? ? ? "state": "CA", ? ? ? ? "zipcode": "94102" ? ? }` ? ? addr := new(address) ? ? json.Unmarshal([]byte(data), &addr) ? ? ? ? // 處理了一番 addr 變量... ? ? addressBytes, _ := json.MarshalIndent(addr, "", " ? ?") ? ? fmt.Printf("%s\n", string(addressBytes)) }
這段代碼的輸出是
{ "street": "200 Larkin St", "suite": "", "city": "San Francisco", "state": "CA", "zipcode": "94102" }
多了一行 "suite": "", ,而這則信息在原本的 json 數(shù)據(jù)中是沒有的(在美國的地址中,如果不是群租公寓或者共享辦公樓, suite 這一條不存在很正常,人們直接用街道門牌號來表示地址就足夠了),但我們更希望的是,在一個地址有 suite 號碼的時候輸出,不存在 suite 的時候就不輸出,幸運的是,我們可以在 Golang 的結(jié)構(gòu)體定義中添加 omitempty 關(guān)鍵字,來表示這條信息如果沒有提供,在序列化成 json 的時候就不要包含其默認值。稍作修改,地址結(jié)構(gòu)體就變成了
type address struct { Street string `json:"street"` Ste string `json:"suite,omitempty"` City string `json:"city"` State string `json:"state"` Zipcode string `json:"zipcode"` }
重新運行,即可得到正確的結(jié)果。
陷阱
帶來方便的同時,使用 omitempty 也有些小陷阱,一個是該關(guān)鍵字無法忽略掉嵌套結(jié)構(gòu)體。還是拿地址類型說事,這回我們想要往地址結(jié)構(gòu)體中加一個新 field 來表示經(jīng)緯度,如果沒有缺乏相關(guān)的數(shù)據(jù),暫時可以忽略。新的 struct 定義如下所示
type address struct { ? ? Street ? ? string ? ? `json:"street"` ? ? Ste ? ? ? ?string ? ? `json:"suite,omitempty"` ? ? City ? ? ? string ? ? `json:"city"` ? ? State ? ? ?string ? ? `json:"state"` ? ? Zipcode ? ?string ? ? `json:"zipcode"` ? ? Coordinate coordinate `json:"coordinate,omitempty"` } type coordinate struct { ? ? Lat float64 `json:"latitude"` ? ? Lng float64 `json:"longitude"` }
讀入原來的地址數(shù)據(jù),處理后序列化輸出,我們就會發(fā)現(xiàn)即使加上了 omitempty 關(guān)鍵字,輸出的 json 還是帶上了一個空的坐標信息
{ "street": "200 Larkin St", "city": "San Francisco", "state": "CA", "zipcode": "94102", "coordinate": { "latitude": 0, "longitude": 0 } }
為了達到我們想要的效果,可以把坐標定義為指針類型,這樣 Golang 就能知道一個指針的“空值”是多少了,否則面對一個我們自定義的結(jié)構(gòu), Golang 是猜不出我們想要的空值的。于是有了如下的結(jié)構(gòu)體定義
type address struct { ? ? Street ? ? string ? ? ?`json:"street"` ? ? Ste ? ? ? ?string ? ? ?`json:"suite,omitempty"` ? ? City ? ? ? string ? ? ?`json:"city"` ? ? State ? ? ?string ? ? ?`json:"state"` ? ? Zipcode ? ?string ? ? ?`json:"zipcode"` ? ? Coordinate *coordinate `json:"coordinate,omitempty"` } type coordinate struct { ? ? Lat float64 `json:"latitude"` ? ? Lng float64 `json:"longitude"` }
相應的輸出為
{ "street": "200 Larkin St", "city": "San Francisco", "state": "CA", "zipcode": "94102" }
另一個“陷阱”是,對于用 omitempty 定義的 field ,如果給它賦的值恰好等于默認空值的話,在轉(zhuǎn)為 json 之后也不會輸出這個 field 。比如說上面定義的經(jīng)緯度坐標結(jié)構(gòu)體,如果我們將經(jīng)緯度兩個 field 都加上 omitempty
type coordinate struct { Lat float64 `json:"latitude,omitempty"` Lng float64 `json:"longitude,omitempty"` }
然后我們對非洲幾內(nèi)亞灣的“原點坐標”非常感興趣,于是編寫了如下代碼
func main() { ? ? cData := `{ ? ? ? ? "latitude": 0.0, ? ? ? ? "longitude": 0.0 ? ? }` ? ? c := new(coordinate) ? ? json.Unmarshal([]byte(cData), &c) ? ? ? ? // 具體處理邏輯... ? ? coordinateBytes, _ := json.MarshalIndent(c, "", " ? ?") ? ? fmt.Printf("%s\n", string(coordinateBytes)) }
最終我們得到了一個
{}
這個坐標消失不見了!但我們的設(shè)想是,如果一個地點沒有經(jīng)緯度信息,則懸空,這沒有問題,但對于“原點坐標”,我們在確切知道它的經(jīng)緯度的情況下,(0.0, 0.0)仍然被忽略了。正確的寫法也是將結(jié)構(gòu)體內(nèi)的定義改為指針
type coordinate struct { ? ? Lat *float64 `json:"latitude,omitempty"` ? ? Lng *float64 `json:"longitude,omitempty"` }
這樣空值就從 float64 的 0.0 變?yōu)榱酥羔橆愋偷?nil ,我們就能看到正確的經(jīng)緯度輸出。
{ "latitude": 0, "longitude": 0 }
P.S. 本文中拿來作示例的地址是舊金山亞洲藝術(shù)博物館的地址,藏品豐富,上到夏商周,下至明清的文物都能看到,幾年前第一次去參觀,很是喜歡,印象深刻。
原文鏈接:https://www.jianshu.com/p/a2ed0d23d1b0
相關(guān)推薦
- 2023-11-21 NVIDIA jetson nano/ Linux/ Ubuntu18.0.4 配置固定IP靜態(tài)IP
- 2022-04-22 appium報錯:Original error: socket hang up
- 2023-10-16 基于lodop實現(xiàn)web端打印分頁樣式自定義配置需求
- 2022-10-23 redis如何實現(xiàn)清空緩存_Redis
- 2022-09-21 ubuntu22通過docker安裝wechat啟動后無界面的問題及解決方法_docker
- 2022-08-29 Python通用驗證碼識別OCR庫ddddocr的安裝使用教程_python
- 2022-07-30 jQuery?UI旋轉(zhuǎn)器部件Spinner?Widget_jquery
- 2023-05-16 移動端開發(fā)之Jetpack?Hilt技術(shù)實現(xiàn)解耦_Android
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支