转换 int64 字节时出现 JSON Marshaller 错误

问题描述

我正在写一个 JSON Marshal 的时间别名到 Unix 格式(在那里留下了我的一些实验测试代码

type NotifyTime time.Time

// MarshalJSON implements marshaller interface. Marshals time.Time into unix time in bytes
func (t NotifyTime) MarshalJSON() ([]byte,error) {
    // unixTime := time.Time(t).Unix()
    unixTime := 1626132059 // unixTime := time.Now().Unix()
    buffer := make([]byte,8)
    binary.Putvarint(buffer,int64(unixTime))
    // binary.LittleEndian.PutUint64(buffer,uint64(unixTime))

    // try to convert back
    fmt.Println(string(buffer))
    unixIntValue := int64(binary.LittleEndian.Uint64(buffer))
    fmt.Println(unixIntValue)

    return buffer,nil
}

当我在具有 NotifyTime 结构的对象上运行 json.Marshal 时,它出错了,如下所示, json: 错误调用 MarshalJSON 类型的 notify.NotifyTime: 无效字符 '¶' 寻找值的开头

        type TestMe struct {
            Time NotifyTime `json:"time"`
        }
        testJSON := TestMe{
            Time: NotifyTime(time.Now()),}
        marshalled,err := json.Marshal(testJSON)

我已经转为将其编组为字符串,但仍然很好奇为什么会发生这种情况。在逐步执行代码时,似乎是因为 关于 go/1.16.2/libexec/src/encoding/json/indent.go:17 上的函数压缩 它在 JSON 的编组字节上循环 并且第一个(第 0 个)字节未通过检入 go/1.16.2/libexec/src/encoding/json/scanner.go:247

解决方法

让我们把编码 time.Time 的方面放在一边,让我们专注于 int64 是如何变成 JSON 的。

binary.PutVarint 使用适合低级线或文件格式的编码。对于常量 1626132059,这将写入缓冲区 [182 185 230 142 12 0 0 0]。第一个字符是 UTF-8 中的 182 PILCROW SIGN。这就是“¶”的来源。您收到如下错误:

json: error calling MarshalJSON for type main.NotifyTime: invalid character '¶' looking for beginning of value

这不是有效 JSON 值的开头。您需要找到一个 JSON 值的 int64 编码,例如十进制数 1626132059 或一串十六进制数字 "60ecce5b"

一般来说,将二进制字符串值放入 JSON 时需要小心,因为这些值可能包含需要转义的特殊字符。