JSON Marshal uint 或 int 作为整数

标签 json go marshalling

我正在寻找有关 json marshal with Go 的信息。我先说明一下情况。

我正在为 IoT 设备开发应用程序。该应用程序将 MQTT 数据包中的 JSON 发送给我们的代理。设备如何使用 SIM 卡进行数据连接我需要将数据包的字节数减少到最少。

现在,JSON 具有这种结构

{
  "d": 1524036831
  "p": "important message"
}

d 字段是时间戳,p 是有效负载。

当应用发送此 JSON 时,它有 40 个字节。但是如果 d 是 1000,pe,JSON 将是 34 字节。所以 marshal 将字段 d 转换为 uint32 到数字的 ASCII 表示,然后发送字符串。

我想要的是将此字段作为 true int 或 uint 发送。我想说,1524036831是一个int32,4个字节,和1000一样。因此,通过此更改,我可以将数据包大小减少一些字节,并且该数字能够增长到 32 位。

我阅读了 json.Marshal 的文档,但没有找到任何相关信息。

我找到了一个“解决方案”,但我认为它不是很漂亮,但可以解决问题。我想要其他意见。

丑陋的解决方案(对我来说)

package main

import (
    "encoding/binary"
    "encoding/json"
    "fmt"
)

type test struct {
    Data    uint32 `json:"d"`
    Payload string `json:"p"`
}
type testB struct {
    Data    []byte `json:"d"`
    Payload string `json:"p"`
}

func main() {
    fmt.Println("TEST with uin32")
    d := []test{test{Data: 5, Payload: "Important Message"}, test{Data: 10, Payload: "Important Message"}, test{Data: 1000, Payload: "Important Message"}, test{Data: 1524036831, Payload: "Important Message"}}
    for _, i := range d {
        j, _ := json.Marshal(i)
        fmt.Println(string(j))
        fmt.Println("All:", len(j))
        fmt.Println("-----------")
    }
    fmt.Println("\nTEST with []Byte")
    d1 := []testB{testB{Data: make([]byte, 4), Payload: "Important Message"}, testB{Data: make([]byte, 4), Payload: "Important Message"}, testB{Data: make([]byte, 4), Payload: "Important Message"}, testB{Data: make([]byte, 4), Payload: "Important Message"}}
    binary.BigEndian.PutUint32(d1[0].Data, 5)
    binary.BigEndian.PutUint32(d1[1].Data, 20)
    binary.BigEndian.PutUint32(d1[2].Data, 1000)
    binary.BigEndian.PutUint32(d1[3].Data, 1524036831)
    for _, i := range d1 {
        j, _ := json.Marshal(i)
        fmt.Println(string(j))
        fmt.Println(len(j))
        fmt.Println("-----------")
    }
}

Play

最佳答案

重申一下我的评论:JSON 是一种文本格式,而文本格式并不是为了生成小消息而设计的。特别是在 JSON 中除了十进制字符串之外没有数字表示。

以大于 10 的基数编码数字将减少足够大数字的消息大小。

您可以通过删除前导零字节并使用 base64.RawStdEncoding(省略填充字符)进行编码来减少“丑陋”代码生成的消息大小。这样做对数字 >= 1e6 是有好处的。

如果你把这一切都放在一个自定义类型中,它会变得更好用:

package main

import (
        "bytes"
        "encoding/base64"
        "encoding/binary"
        "encoding/json"
        "fmt"
)

type IntB64 uint32

func (n IntB64) MarshalJSON() ([]byte, error) {
        b := make([]byte, 4)

        binary.BigEndian.PutUint32(b, uint32(n))
        b = bytes.TrimLeft(b, string(0))

        // All characters in the base64 alphabet need not be escaped, so we don't
        // have to call json.Marshal here.
        l := base64.RawStdEncoding.EncodedLen(len(b)) + 2
        j := make([]byte, l)

        base64.RawStdEncoding.Encode(j[1:], b)
        j[0], j[l-1] = '"', '"'

        return j, nil
}

func main() {
        enc(1)          // "AQ"
        enc(1000)       // "A+g"
        enc(1e6 - 1)    // "D0I/"
        enc(1e6)        // "D0JA"
        enc(1524036831) // "Wtb03w"
}

func enc(n int64) {
        b, _ := json.Marshal(IntB64(n))
        fmt.Printf("%10d %s\n", n, string(b))
}

更新 Playground :https://play.golang.org/p/7Z03VE9roqN

关于JSON Marshal uint 或 int 作为整数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49896677/

相关文章:

arrays - 如何使用jq将对象列表转换为列表对象?

javascript - 将 JSON 数据从 Web API 映射到 KnockOut

go - VS Code 抛出错误 'No package for import' 但代码编译并运行良好

go - Go 可以避免中间接口(interface)吗?

go - 登录 Golang 测试用例

json - 在 Linux 上使用 gaction 更新 Google Home/Assistant 包时,Golang 运行时出现 panic ?

javascript - 如何将用户推送到对象,PHP 到 JSON

c# - 编码时打包如何工作?

c# - 我应该如何为这段代码指定 P/invoke 接口(interface)?

java - 解码时不使用 JAXB 自己的对象工厂