成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

JSON包新提案:用“omitzero”解決編碼中的空值困局

開(kāi)發(fā) 前端
通過(guò)引入"omitzero"標(biāo)簽,Go語(yǔ)言在解決JSON編碼中"空"值處理的痛點(diǎn)上邁出了重要一步。這個(gè)方案不僅滿足了開(kāi)發(fā)者對(duì)更靈活的"空"值定義的需求,還保持了與現(xiàn)有系統(tǒng)的兼容性。

Go標(biāo)準(zhǔn)庫(kù)是Go號(hào)稱“開(kāi)箱即用”的重要因素,而標(biāo)準(zhǔn)庫(kù)中的encoding/json包又是標(biāo)準(zhǔn)庫(kù)最常用的Go包。雖然其性能不是最好的,但好在由Go團(tuán)隊(duì)維護(hù),對(duì)JSON規(guī)范兼容性好,且質(zhì)量很高。但json包也不是沒(méi)有“瑕疵”的,Go官方繼math/rand/v2[1]之后,也開(kāi)啟了encoding/json/v2的討論[2],v2包含了對(duì)功能的增強(qiáng),其中就包含了對(duì)空值編碼的改進(jìn)的考量,以及性能方面的優(yōu)化。但json/v2畢竟還屬于“長(zhǎng)遠(yuǎn)”規(guī)劃,當(dāng)前版本的json包的問(wèn)題也要修正和完善。

一個(gè)提出于2021年的issue[3]近期被即將“功成身退”的Russ Cox[4],該issue就當(dāng)前json包對(duì)空值編碼的“瑕疵”做了描述并提出了修正方案。本文就將針對(duì)這一問(wèn)題以及其方案進(jìn)行探討,希望能幫助大家更好地理解該issue以及其對(duì)應(yīng)的方案。

1. 問(wèn)題溯源:omitempty的局限性

在encoding/json包中,omitempty標(biāo)簽是開(kāi)發(fā)者控制JSON序列化行為的重要工具。它的設(shè)計(jì)初衷是允許開(kāi)發(fā)者指定:當(dāng)某個(gè)字段值為“空”時(shí),在JSON編碼過(guò)程中應(yīng)該被忽略。然而,omitempty的空值定義存在一些固有的局限性。下面是json包中對(duì)omitempty的說(shuō)明:

The "omitempty" option specifies that the field should be omitted from the encoding if the field has an empty value, defined as false, 0, a nil pointer, a nil interface value, and any empty array, slice, map, or string.

總結(jié)一下,omitempty標(biāo)簽的判斷邏輯如下:

  • 對(duì)于布爾類型:false被視為空
  • 對(duì)于數(shù)值類型:0被視為空
  • 對(duì)于字符串:""(空字符串)被視為空
  • 對(duì)于指針、接口:nil被視為空
  • 對(duì)于數(shù)組、切片、map:長(zhǎng)度為0被視為空

下面是一個(gè)完整的Go示例,展示了omitempty標(biāo)簽在不同類型上的應(yīng)用:

package main

import (
 "encoding/json"
 "fmt"
)

type Example struct {
 BoolField      bool           `json:"bool_field,omitempty"`
 IntField       int            `json:"int_field,omitempty"`
 StringField    string         `json:"string_field,omitempty"`
 PointerField   *string        `json:"pointer_field,omitempty"`
 InterfaceField interface{}    `json:"interface_field,omitempty"`
 ArrayField     [0]int         `json:"array_field,omitempty"` // 空數(shù)組
 SliceField     []string       `json:"slice_field,omitempty"` // 空切片
 MapField       map[string]int `json:"map_field,omitempty"`   // 空地圖
}

func main() {
 var nilString *string = nil

 example := Example{
  BoolField:      false,            // 布爾類型
  IntField:       0,                // 數(shù)值類型
  StringField:    "",               // 空字符串
  PointerField:   nilString,        // nil 指針
  InterfaceField: nil,              // nil 接口
  ArrayField:     [0]int{},         // 空數(shù)組
  SliceField:     []string{},       // 空切片
  MapField:       map[string]int{}, // 空地圖
 }

 jsonData, err := json.Marshal(example)
 if err != nil {
  fmt.Println("Error marshalling example:", err)
 }
 fmt.Println(string(jsonData)) // 輸出:{}
}

然而,這種預(yù)定義的"空"值判斷邏輯并不能滿足所有實(shí)際場(chǎng)景的需求。讓我們來(lái)看幾個(gè)具體的例子:

  • 空結(jié)構(gòu)體問(wèn)題
package main

import (
 "encoding/json"
 "fmt"
)

type Config struct {
 EmptyStruct struct{} `json:",omitempty"`
}

func main() {
 cfg := Config{}
 data, _ := json.Marshal(cfg)
 fmt.Println(string(data)) // 輸出:{"EmptyStruct":{}}
}

我們看到:在這個(gè)例子中,盡管Config中的EmptyStruct字段是一個(gè)空結(jié)構(gòu)體類型,且添加了omitempty標(biāo)簽,但它仍然出現(xiàn)在JSON輸出中。

  • 零值結(jié)構(gòu)體

除了空結(jié)構(gòu)體,零值結(jié)構(gòu)體也是目前omitempty標(biāo)簽語(yǔ)義覆蓋不到的類型:

package main

import (
 "encoding/json"
 "fmt"
)

type ZeroStruct struct {
 A int
 B string
 C float64
}

type Config struct {
 ZeroStruct ZeroStruct `json:",omitempty"`
}

func main() {
 cfg := Config{}
 data, _ := json.Marshal(cfg)
 fmt.Println(string(data)) // 輸出:{"ZeroStruct":{"A":0,"B":"","C":0}}
}

我們看到:即便ZeroStruct中各個(gè)類型的值都為零,且有了omitempty標(biāo)簽,json.Marshal依然輸出了Config中的ZeroStruct字段。

  • time.Time類型的處理

在開(kāi)發(fā)實(shí)踐中,我們發(fā)現(xiàn)json對(duì)time.Time類型在omitempty下的處理也與“常理”不符,比如下面這個(gè)示例:

package main

import (
 "encoding/json"
 "fmt"
 "time"
)

type Event struct {
 Time time.Time `json:",omitempty"`
}

func main() {
 evt := Event{Time: time.Time{}} // 零值時(shí)間
 data, _ := json.Marshal(evt)
 fmt.Println(string(data)) // 輸出:{"Time":"0001-01-01T00:00:00Z"}
}

我們看到:time.Time類型的零值依然被輸出了。并且輸出的是公元1年1月1日UTC時(shí)間。對(duì)于很多應(yīng)用來(lái)說(shuō),這個(gè)時(shí)間并不具有實(shí)際意義,更合理的零值是"January 01, 1970 00:00:00 UTC"。

很顯然,Gopher們希望json包能更好的處理上述情形。

2. 社區(qū)討論與omitzero標(biāo)簽方案的確認(rèn)

關(guān)于上述問(wèn)題的解決方法,在Go社區(qū)引發(fā)了廣泛討論。不過(guò)大家普遍認(rèn)為不要改變現(xiàn)有omitempty語(yǔ)義,那樣會(huì)導(dǎo)致破壞性的change,無(wú)法向后兼容。

在討論過(guò)程中,社區(qū)成員提出了一些其他的解決方案:

  • 允許MarshalJSON()方法返回nil來(lái)完全忽略某個(gè)字段

這個(gè)方案的優(yōu)點(diǎn)是利用了已有的接口,不需要引入新的標(biāo)簽。但缺點(diǎn)是需要為每個(gè)支持零值的類型都實(shí)現(xiàn)MarshalJSON()方法。

  • 添加OmitJSONField方法

這個(gè)方案提議為每個(gè)類型添加一個(gè)OmitJSONField() bool方法,由開(kāi)發(fā)者自己控制字段的忽略邏輯,該方案提供了很大的靈活性,但可能會(huì)導(dǎo)致JSON序列化邏輯過(guò)于分散。

最終,"omitzero"方案最終被認(rèn)為是一個(gè)相對(duì)平衡的解決方案,因?yàn)樗梢耘c現(xiàn)有的標(biāo)簽系統(tǒng)兼容,開(kāi)發(fā)者可以很容易地將omitempty替換為omitzero,或者在需要的地方同時(shí)使用兩者。此外,omitzero也保持了簡(jiǎn)潔性,相比其他需要大量代碼修改的方案,omitzero只需要添加標(biāo)簽或?qū)崿F(xiàn)一個(gè)方法(可選項(xiàng))即可。

"omitzero"標(biāo)簽提案的核心內(nèi)容是:在序列化時(shí),"omitzero"選項(xiàng)指定如果字段值為零,則該結(jié)構(gòu)體字段應(yīng)被省略。如果該類型定義了IsZero bool方法,那么這個(gè)零值就通過(guò)IsZero方法來(lái)判斷;否則是根據(jù)字段是否是零值(通過(guò)reflect.Value.IsZero判斷)來(lái)判斷。該omitzero選項(xiàng)在反序列化(unmarshal)時(shí)沒(méi)有效果。如果同時(shí)指定了"omitempty"和"omitzero",則字段是否被省略基于兩者的邏輯或關(guān)系。 這將意味著,在省略切片時(shí),omitzero會(huì)省略空指針切片,但對(duì)于長(zhǎng)度為零的非空切片,則不會(huì)。對(duì)于time.Time類型,會(huì)省略time.Time{}。

此外,omitzero不強(qiáng)制你實(shí)現(xiàn)IsZero方法,但開(kāi)發(fā)者可以利用IsZero方法來(lái)自由控制自定義類型在omitzero標(biāo)簽下是否會(huì)被省略。

一旦有了omitzero,我們就可以用它解決上面提到的問(wèn)題(omitzero尚未實(shí)現(xiàn),下面是偽代碼):

  • 解決空結(jié)構(gòu)體問(wèn)題
type Config struct {
    EmptyStruct struct{} `json:",omitzero"`
}

cfg := Config{}
data, _ := json.Marshal(cfg)
fmt.Println(string(data)) // 輸出:{}
  • 更好地處理time.Time類型
type Event struct {
    Time time.Time `json:",omitzero"`
}

evt := Event{Time: time.Time{}} // 零值時(shí)間
data, _ := json.Marshal(evt)
fmt.Println(string(data)) // 輸出:{}
  • 自定義類型的"零值"判斷
type CustomInt int

func (ci CustomInt) IsZero() bool {
    return ci <= 0 // 自定義零值判斷邏輯
}

type Data struct {
    Value CustomInt `json:",omitzero"`
}

d := Data{Value: CustomInt(-1)}
data, _ := json.Marshal(d)
fmt.Println(string(data)) // 輸出:{}

3. 小結(jié)

通過(guò)引入"omitzero"標(biāo)簽,Go語(yǔ)言在解決JSON編碼中"空"值處理的痛點(diǎn)上邁出了重要一步。這個(gè)方案不僅滿足了開(kāi)發(fā)者對(duì)更靈活的"空"值定義的需求,還保持了與現(xiàn)有系統(tǒng)的兼容性。目前該omitzero的落地時(shí)間尚未確定,最早也要等到Go 1.24版本。此外,encoding/xml等也會(huì)效仿json包,增加omitzero標(biāo)簽。

此外,伴隨著omitzero提案被接受,另外一個(gè)在2021年由Josh Bleecher Snyder提出的相關(guān)提案:proposal: cmd/vet: warn about structs marked json omitempty[5]也被重新“喚醒”,針對(duì)該提案,目前社區(qū)在active discussions。

隨著后續(xù)encoding/json/v2的到來(lái),我們可以期待Go語(yǔ)言在數(shù)據(jù)序列化領(lǐng)域會(huì)有更出色的表現(xiàn)。這不僅將提升json編解碼效率,還將為構(gòu)建更加健壯和靈活的基于json的Go應(yīng)用程序鋪平了道路。

責(zé)任編輯:武曉燕 來(lái)源: TonyBai
相關(guān)推薦

2021-12-27 07:59:50

ECMAScript JSON模塊Node.js

2021-08-09 10:36:20

GoSlices Maps

2023-09-21 11:39:29

RustJetBrainsIDE

2022-07-13 08:53:28

函數(shù)Go語(yǔ)言

2024-06-05 08:47:20

Go語(yǔ)言方式

2021-02-25 15:51:41

Go語(yǔ)言模糊測(cè)試功能

2022-01-11 12:13:33

JavaScript編程語(yǔ)言

2022-06-08 10:46:00

CSS前端

2017-05-23 09:03:16

2024-11-19 09:10:19

迭代器Go語(yǔ)言

2023-12-27 08:03:53

Go優(yōu)化代碼

2021-07-27 13:08:52

微軟Chrome新提案

2014-09-29 16:44:12

2010-08-26 10:20:43

2023-08-30 13:23:12

異步編程JavaScript

2022-11-15 09:16:59

2021-12-13 08:52:42

Go 泛型

2025-02-08 09:57:20

2022-05-30 10:09:27

技術(shù)債

2009-12-04 13:31:24

PHP缺少JSON包
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 精品福利一区二区三区 | 久久国内 | 国产a区| 日本精品视频 | 嫩草最新网址 | 九九国产 | 久久久国产精品入口麻豆 | 91在线精品一区二区 | 亚洲人人| 不卡的av在线 | 91一区二区三区在线观看 | 精品久久一 | 久久综合婷婷 | 久久久精品综合 | 羞羞色视频| 中文字幕在线一 | 亚洲444kkkk在线观看最新 | 亚洲精品乱码久久久久久蜜桃 | 国产伦一区二区三区四区 | 久久久久久久久久影视 | 狠狠操电影 | 欧美xxxx色视频在线观看免费 | 国产精品1区 | 国产精品永久久久久久久www | 国产精品国产精品国产专区不卡 | 中文字幕久久精品 | 久久免费观看视频 | 99国产精品久久久 | 羞羞视频在线观免费观看 | 美日韩一区二区 | 色综合久久天天综合网 | 欧美男人天堂 | 欧美成年黄网站色视频 | 九九视频在线观看视频6 | 成人无遮挡毛片免费看 | 在线免费观看黄a | 久久中文一区二区 | 在线成人av | 国产成人精品久久二区二区 | 亚洲精品美女视频 | 亚洲综合五月天婷婷 |