V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
stevenshuang

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

  •  
  •   stevenshuang · Aug 30, 2023 · 3585 views
    This topic created in 983 days ago, the information mentioned may be changed or developed.

    求助:

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

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

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

    ```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  
       Aug 30, 2023
    前段时间写了个 bencode 编解码库,.torrent 也是,tracker 字段可能是数组或者字符串,最后用 interface 解决。
    stevenshuang
        7
    stevenshuang  
    OP
       Aug 30, 2023
    @xlsepiphone 那我也还是先用 interface 吧。
    bianxiaoxian
        8
    bianxiaoxian  
       Aug 30, 2023
    golang 非常好用的 JSON 解析库,可以直接获取单个值: https://github.com/tidwall/gjson
    stevenshuang
        9
    stevenshuang  
    OP
       Aug 30, 2023
    @morebuff 感谢,我试试😁
    iyaozhen
        10
    iyaozhen  
       Aug 30, 2023
    先变成 map interface ,然后判断字段,再转成对应结构体

    https://github.com/mitchellh/mapstructure
    Rehtt
        11
    Rehtt  
       Aug 31, 2023   ❤️ 1
    https://github.com/json-iterator/go

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

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

    比如 kubernetes 里就有类似的实现。https://pkg.go.dev/k8s.io/apimachinery/pkg/util/intstr#IntOrString
    rammiah
        18
    rammiah  
       Aug 31, 2023
    wuqiangroy
        19
    wuqiangroy  
       Aug 31, 2023
    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  
       Aug 31, 2023
    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  
       Aug 31, 2023   ❤️ 1
    @cheng6563 @wuqiangroy
    JSON 标准( RFC 8259 )对数字精度是没有限制的,可由具体实现自定义;只是说 double 类型用得更广泛,所以使用 double 会有更好的互操作性。
    darkengine
        22
    darkengine  
       Aug 31, 2023
    @stevenshuang "如果还包含其他类型"

    -------

    这种情况不应该找对方打一架吗?
    herozzm
        23
    herozzm  
       Aug 31, 2023 via iPhone
    用 gjson
    xsen
        24
    xsen  
       Aug 31, 2023
    gjson/sjson
    wqtacc
        25
    wqtacc  
       Aug 31, 2023
    不折腾,用 float64
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2516 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 61ms · UTC 00:29 · PVG 08:29 · LAX 17:29 · JFK 20:29
    ♥ Do have faith in what you're doing.