亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何在 JSON 中自定義封送映射鍵

如何在 JSON 中自定義封送映射鍵

Go
胡說叔叔 2023-03-29 16:11:41
我無法理解 custom marshal intto的奇怪行為string。這是一個例子:package mainimport (    "encoding/json"    "fmt")type Int intfunc (a Int) MarshalJSON() ([]byte, error) {    test := a / 10    return json.Marshal(fmt.Sprintf("%d-%d", a, test))}func main() {    array := []Int{100, 200}    arrayJson, _ := json.Marshal(array)    fmt.Println("array", string(arrayJson))    maps := map[Int]bool{        100: true,        200: true,    }    mapsJson, _ := json.Marshal(maps)    fmt.Println("map wtf?", string(mapsJson))    fmt.Println("map must be:", `{"100-10":true, "200-20":true}`)}輸出是:array ["100-10","200-20"]map wtf? {"100":true,"200":true}map must be: {"100-10":true, "200-20":true}https://play.golang.org/p/iiUyL2Hc5h_P我錯過了什么?
查看完整描述

2 回答

?
絕地無雙

TA貢獻1946條經驗 獲得超4個贊

這是預期的結果,記錄在json.Marshal():


映射值編碼為 JSON 對象。映射的鍵類型必須是字符串、整數類型或實現 encoding.TextMarshaler。通過應用以下規則對映射鍵進行排序并將其用作 JSON 對象鍵,并遵守針對上述字符串值描述的 UTF-8 強制轉換:


- string keys are used directly

- encoding.TextMarshalers are marshaled

- integer keys are converted to strings

請注意,映射鍵的處理方式與屬性值的處理方式不同,因為 JSON 中的映射鍵是始終為值的屬性名稱string(而屬性值可能是 JSON 文本、數字和布爾值)。


根據文檔,如果您希望它也適用于地圖鍵,請實施encoding.TextMarshaler:


func (a Int) MarshalText() (text []byte, err error) {

    test := a / 10

    return []byte(fmt.Sprintf("%d-%d", a, test)), nil

}

(請注意,它MarshalText()應該返回“只是”簡單文本,而不是 JSON 文本,因此我們在其中省略了 JSON 封送處理!)


這樣,輸出將是(在Go Playground上嘗試):


array ["100-10","200-20"] <nil>

map wtf? {"100-10":true,"200-20":true} <nil>

map must be: {"100-10":true, "200-20":true}

請注意,這就encoding.TextMarshaler足夠了,因為在編組為值時也會檢查它,而不僅僅是映射鍵。所以你不必同時實現encoding.TextMarshaler和json.Marshaler。


如果你同時實現了這兩者,當值被編組為“簡單”值和映射鍵時,你可以有不同的輸出,因為json.Marshaler在生成值時優先:


func (a Int) MarshalJSON() ([]byte, error) {

    test := a / 100

    return json.Marshal(fmt.Sprintf("%d-%d", a, test))

}


func (a Int) MarshalText() (text []byte, err error) {

    test := a / 10

    return []byte(fmt.Sprintf("%d-%d", a, test)), nil

}

這次輸出將是(在Go Playground上試試):


array ["100-1","200-2"] <nil>

map wtf? {"100-10":true,"200-20":true} <nil>

map must be: {"100-10":true, "200-20":true}


查看完整回答
反對 回復 2023-03-29
?
侃侃爾雅

TA貢獻1801條經驗 獲得超16個贊

接受的答案很好,但我不得不重新搜索足夠多的時間,所以我想通過示例給出關于編組/解組的完整答案,所以下次我可以只復制粘貼作為起點:)

我經常搜索的內容包括:

  • 將自定義類型編碼到 sql 數據庫

  • json 將 enum int 編碼為字符串

  • json編碼映射鍵但不編碼值

在這個例子中,我創建了一個自定義的 Weekday 類型,它匹配 time.Weekday int 值,但允許請求/響應 json 和數據庫中的字符串值

同樣的事情可以用任何使用 iota 的 int 枚舉來完成,以便在 json 和數據庫中具有人類可讀的值

游樂場測試示例https://go.dev/play/p/aUxxIJ6tY9K

重要的一點在這里:

var (

    // read/write from/to json values

    _ json.Marshaler   = (*Weekday)(nil)

    _ json.Unmarshaler = (*Weekday)(nil)


    // read/write from/to json keys

    _ encoding.TextMarshaler   = (*Weekday)(nil)

    _ encoding.TextUnmarshaler = (*Weekday)(nil)


    // read/write from/to sql

    _ sql.Scanner   = (*Weekday)(nil)

    _ driver.Valuer = (*Weekday)(nil)

)


// MarshalJSON marshals the enum as a quoted json string

func (w Weekday) MarshalJSON() ([]byte, error) {

    return []byte(`"` + w.String() + `"`), nil

}


func (w Weekday) MarshalText() (text []byte, err error) {

    return []byte(w.String()), nil

}


func (w *Weekday) UnmarshalJSON(b []byte) error {

    return w.UnmarshalText(b)

}


func (w *Weekday) UnmarshalText(b []byte) error {

    var dayName string

    if err := json.Unmarshal(b, &dayName); err != nil {

        return err

    }


    d, err := ParseWeekday(dayName)

    if err != nil {

        return err

    }


    *w = d

    return nil

}


// Value is used for sql exec to persist this type as a string

func (w Weekday) Value() (driver.Value, error) {

    return w.String(), nil

}


// Scan implements sql.Scanner so that Scan will be scanned correctly from storage

func (w *Weekday) Scan(src interface{}) error {

    switch t := src.(type) {

    case int:

        *w = Weekday(t)

    case int64:

        *w = Weekday(int(t))

    case string:

        d, err := ParseWeekday(t)

        if err != nil {

            return err

        }

        *w = d

    case []byte:

        d, err := ParseWeekday(string(t))

        if err != nil {

            return err

        }

        *w = d

    default:

        return errors.New("Weekday.Scan requires a string or byte array")

    }

    return nil

}


請注意,var 塊只是強制您正確地實現這些方法,否則它不會編譯。


另請注意,如果您排除MarshalJSON然后 go 將在它存在時使用MarshalText,因此如果您只希望鍵具有自定義編組但具有值的默認行為那么您不應該在您的主要類型上使用這些方法,而是具有僅用于映射鍵的包裝器類型


type MyType struct{}

type MyTypeKey MyType

var (

    // read/write from/to json keys

    _ encoding.TextMarshaler   = (*MyTypeKey)(nil)

    _ encoding.TextUnmarshaler = (*MyTypeKey)(nil)

)

func (w MyTypeKey) MarshalText() (text []byte, err error) {

    return []byte(w.String()), nil

}


func (w *MyTypeKey) UnmarshalText(b []byte) error {

    *w = MyTypeKey(ParseMyType(string(b)))

    return nil

}

隨意改進這個答案,我希望其他人發現它有幫助,我希望下次我能再次找到它并自己再次搜索它:)


查看完整回答
反對 回復 2023-03-29
  • 2 回答
  • 0 關注
  • 112 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號