如何在JSON中自定义封送映射键

Mar*_*one 2 string json dictionary go slice

我无法理解的自定义元帅的一个奇怪的行为intstring.

这是一个例子:

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}`)
}
Run Code Online (Sandbox Code Playgroud)

输出是:

array ["100-10","200-20"]
map wtf? {"100":true,"200":true}
map must be: {"100-10":true, "200-20":true}
Run Code Online (Sandbox Code Playgroud)

https://play.golang.org/p/iiUyL2Hc5h_P

我错过了什么?

icz*_*cza 7

这是预期的结果,记录在json.Marshal():

映射值编码为JSON对象.映射的键类型必须是字符串,整数类型或实现encoding.TextMarshaler.通过应用以下规则对映射键进行排序并将其用作JSON对象键,具体取决于上面对字符串值所描述的UTF-8强制:

- string keys are used directly
- encoding.TextMarshalers are marshaled
- integer keys are converted to strings
Run Code Online (Sandbox Code Playgroud)

请注意,映射键的处理方式与属性值的处理方式不同,因为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
}
Run Code Online (Sandbox Code Playgroud)

(注意,MarshalText()应该返回"just"简单文本,而不是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}
Run Code Online (Sandbox Code Playgroud)

请注意,这encoding.TextMarshaler足以在作为值进行marsaling时检查,而不仅仅是映射键.所以你不必同时实现encoding.TextMarshalerjson.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
}
Run Code Online (Sandbox Code Playgroud)

这次输出将是(在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}
Run Code Online (Sandbox Code Playgroud)