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

如何在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 乌班图ip地址配置及远程SSH
  • ¥15 怎么让点阵屏显示静态爱心,用keiluVision5写出让点阵屏显示静态爱心的代码,越快越好
  • ¥15 PSPICE制作一个加法器
  • ¥15 javaweb项目无法正常跳转
  • ¥15 VMBox虚拟机无法访问
  • ¥15 skd显示找不到头文件
  • ¥15 机器视觉中图片中长度与真实长度的关系
  • ¥15 fastreport table 怎么只让每页的最下面和最顶部有横线
  • ¥15 java 的protected权限 ,问题在注释里
  • ¥15 这个是哪里有问题啊?