V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
stevenshuang
V2EX  ›  Go 编程语言

Go 如何解析同一个字段可能是多种类型的 json

  •  
  •   stevenshuang · 2023-08-30 22:13:37 +08:00 · 2549 次点击
    这是一个创建于 445 天前的主题,其中的信息可能已经有所发展或是发生改变。

    求助:

    一个 python 的服务端返回 json 数据,但是有一个字段可能是 int ,也可能是 float 。 那么 go(1.21) 该如何处理这种情况呢?

    我想的是 利用 go 的范型一字段可以设置多种情况,但是实际用的时候,还是需要明确结构字段的类型。 或者就是直接用 map[string]any?

    25 条回复    2023-08-31 22:53:17 +08:00
    seers
        1
    seers  
       2023-08-30 22:16:31 +08:00 via Android
    interface 然后断言行吗
    stevenshuang
        2
    stevenshuang  
    OP
       2023-08-30 22:19:28 +08:00
    @seers 这样是可以的,我开始也是用 interface 然后判断类型,但是感觉略麻烦。想着是不是 go 的范型可以解决,但是目前看好像不太行😅
    virusdefender
        3
    virusdefender  
       2023-08-30 22:20:27 +08:00
    如果都是数字的话,声明成 float 就行?或者用 json.Number
    stevenshuang
        4
    stevenshuang  
    OP
       2023-08-30 22:31:23 +08:00
    @virusdefender 如果还包含其他类型,那么相比之下,直接用 interface 来解 json 更方便了吧,用哪个字段判断一下类型。
    stevenshuang
        5
    stevenshuang  
    OP
       2023-08-30 22:32:26 +08:00
    下面是一个结构的定义。帖子不能修改了,把定义放到这里了。

    ```go

    type (
    MetadataType interface {
    ~string | ~int | ~float64 | ~bool
    }

    Embedding interface {
    ~float64 | ~int
    }
    )

    type Metadata[M MetadataType] map[string]M

    type GetResult[M MetadataType, E Embedding] struct {
    IDs []string `json:"ids"`
    Embeddings []E `json:"embeddings,omitempty"`
    Documents []string `json:"documents,omitempty"`
    Metadatas []Metadata[M] `json:"metadatas,omitempty"`
    }
    ```
    xlsepiphone
        6
    xlsepiphone  
       2023-08-30 22:38:40 +08:00
    前段时间写了个 bencode 编解码库,.torrent 也是,tracker 字段可能是数组或者字符串,最后用 interface 解决。
    stevenshuang
        7
    stevenshuang  
    OP
       2023-08-30 22:45:18 +08:00
    @xlsepiphone 那我也还是先用 interface 吧。
    morebuff
        8
    morebuff  
       2023-08-30 23:05:27 +08:00
    golang 非常好用的 JSON 解析库,可以直接获取单个值: https://github.com/tidwall/gjson
    stevenshuang
        9
    stevenshuang  
    OP
       2023-08-30 23:28:59 +08:00
    @morebuff 感谢,我试试😁
    iyaozhen
        10
    iyaozhen  
       2023-08-30 23:33:17 +08:00
    先变成 map interface ,然后判断字段,再转成对应结构体

    https://github.com/mitchellh/mapstructure
    Rehtt
        11
    Rehtt  
       2023-08-31 08:32:32 +08:00   ❤️ 1
    https://github.com/json-iterator/go

    jsoniter.Get([]byte(`{"a": 123,"b": {"c": "cc"}}`),"b").Get("c").ToString()
    lisxour
        12
    lisxour  
       2023-08-31 09:05:53 +08:00
    这种可变结果或者类型的 json 就不应该做成结构体啊,用可以动态获取的库,我觉得满屏的 interface 和在 ts 中满屏的 any 一样无法令人接受
    bv
        13
    bv  
       2023-08-31 09:13:41 +08:00
    还不如用 float64 接收,兼容 int float
    pubby
        14
    pubby  
       2023-08-31 09:46:49 +08:00   ❤️ 1
    用 json.Number ,尤其是会遇到大整数的场景

    用 float64 的坑是遇到大整数,即使整数在 int64 范围内也会有精度损失问题
    用 interface{} 也有问题,默认数字类型就会 decode 成 float64 ,除非 decoder 上用.UseNumber() 强制解析到 json.Number 类型
    mengzhuo
        15
    mengzhuo  
       2023-08-31 09:49:12 +08:00
    标准库可以用 json.RawMessage
    第三方库随意哈
    cheng6563
        16
    cheng6563  
       2023-08-31 09:56:53 +08:00   ❤️ 2
    Json 的数据类型是基于 js 的,所以数字类型只有 float64 ,不存在其他类型。
    joyme
        17
    joyme  
       2023-08-31 09:58:24 +08:00   ❤️ 1
    可以定义一个 struct 来代表不同的类型,然后为这个 struct 实现 Marshal/ Unmarshal 。这样这个 struct 就能代表不同的类型,使用的时候也很方便。

    比如 kubernetes 里就有类似的实现。https://pkg.go.dev/k8s.io/apimachinery/pkg/util/intstr#IntOrString
    yianing
        18
    yianing  
       2023-08-31 10:27:07 +08:00
    wuqiangroy
        19
    wuqiangroy  
       2023-08-31 11:38:12 +08:00
    The core issue is how to convert float64 to int.
    All the numbers in a JSON string are the type of float64.
    wuqiangroy
        20
    wuqiangroy  
       2023-08-31 11:45:58 +08:00
    I provide a suggestion.
    ```golang
    type Res struct {
    Value any `json:"value"` // Int or Float
    FloatValue float64
    IntValue int
    }

    func (r *Res) UnmarshalJSON(source []byte) (err error) {
    // json.number is type of float64
    type Temp struct {
    Value float64 `json:"value"`
    }
    var temp = Temp{}
    decoder := json.NewDecoder(bytes.NewReader(source))
    //means that number convert to json.Number
    decoder.UseNumber()
    //decode json
    if err = decoder.Decode(&temp); err != nil {
    return err
    }
    var convertToInt bool
    var convertValue int
    // convert float64 to int
    if convertToInt {
    r.IntValue = convertValue
    } else {
    r.FloatValue = temp.Value
    }
    return
    }
    ```
    usage:
    ```golang
    func Usage() {
    var source = []byte(`{"value":123}`)
    var res Res
    _ = json.Unmarshal(source, &res)
    // use intValue
    res.IntValue
    // use floatValue
    source = []byte(`{"value":123.23}`)
    _ = json.Unmarshal(source, &res)
    res.FloatValue
    }
    ```
    icyalala
        21
    icyalala  
       2023-08-31 11:48:49 +08:00   ❤️ 1
    @cheng6563 @wuqiangroy
    JSON 标准( RFC 8259 )对数字精度是没有限制的,可由具体实现自定义;只是说 double 类型用得更广泛,所以使用 double 会有更好的互操作性。
    darkengine
        22
    darkengine  
       2023-08-31 12:48:43 +08:00
    @stevenshuang "如果还包含其他类型"

    -------

    这种情况不应该找对方打一架吗?
    herozzm
        23
    herozzm  
       2023-08-31 12:50:05 +08:00 via iPhone
    用 gjson
    xsen
        24
    xsen  
       2023-08-31 16:07:22 +08:00
    gjson/sjson
    wqtacc
        25
    wqtacc  
       2023-08-31 22:53:17 +08:00
    不折腾,用 float64
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   942 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 22:40 · PVG 06:40 · LAX 14:40 · JFK 17:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.