dsaeyrq451928 2018-09-04 08:02
浏览 122
已采纳

如何在JSON中自定义编组映射键

I can't understand a strange behavior of custom marshal int to string.

Here is an example:

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}`)
}

The output is:

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

What am I missing?

  • 写回答

1条回答 默认 最新

  • dsqpx86002 2018-09-04 08:11
    关注

    This is the expected outcome, which is documented at json.Marshal():

    Map values encode as JSON objects. The map's key type must either be a string, an integer type, or implement encoding.TextMarshaler. The map keys are sorted and used as JSON object keys by applying the following rules, subject to the UTF-8 coercion described for string values above:

    - string keys are used directly
    - encoding.TextMarshalers are marshaled
    - integer keys are converted to strings
    

    Note that map keys are handled differently than values of properties because map keys in JSON are the property names which are always string values (while property values may be JSON text, number and boolean values).

    As per the doc, if you want it to work for map keys as well, implement encoding.TextMarshaler:

    func (a Int) MarshalText() (text []byte, err error) {
        test := a / 10
        return []byte(fmt.Sprintf("%d-%d", a, test)), nil
    }
    

    (Note that MarshalText() is ought to return "just" simple text, not JSON text, hence we omit JSON marshaling in it!)

    With this, output will be (try it on the 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}
    

    Note that encoding.TextMarshaler is enough as that is also checked when marsaling as values, not just for map keys. So you don't have to implement both encoding.TextMarshaler and json.Marshaler.

    If you do implement both, you can have different output when the value is marshaled as a "simple" value and as a map key because json.Marshaler takes precedence when generating a value:

    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
    }
    

    This time the output will be (try it on the 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}
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 keil里为什么main.c定义的函数在it.c调用不了
  • ¥50 切换TabTip键盘的输入法
  • ¥15 可否在不同线程中调用封装数据库操作的类
  • ¥15 微带串馈天线阵列每个阵元宽度计算
  • ¥15 keil的map文件中Image component sizes各项意思
  • ¥20 求个正点原子stm32f407开发版的贪吃蛇游戏
  • ¥15 划分vlan后,链路不通了?
  • ¥20 求各位懂行的人,注册表能不能看到usb使用得具体信息,干了什么,传输了什么数据
  • ¥15 Vue3 大型图片数据拖动排序
  • ¥15 Centos / PETGEM