【问题标题】:How to custom marshal map keys in JSON如何在 JSON 中自定义编组映射键
【发布时间】:2019-02-09 04:58:58
【问题描述】:

我无法理解自定义元帅 int 到 string 的奇怪行为。

这是一个例子:

package main

import (
    "encoding/json"
    "fmt"
)

type Int int

func (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

我错过了什么?

【问题讨论】:

    标签: json string dictionary go slice


    【解决方案1】:

    这是预期的结果,记录在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}
    

    【讨论】:

      猜你喜欢
      • 2018-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-05
      • 1970-01-01
      • 2018-05-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多