Golang Go语言中如何解析同一个字段可能是多种类型的JSON

发布于 1周前 作者 yibo5220 来自 Go语言

求助:

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

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


Golang Go语言中如何解析同一个字段可能是多种类型的JSON
26 回复

interface 然后断言行吗

更多关于Golang Go语言中如何解析同一个字段可能是多种类型的JSON的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这样是可以的,我开始也是用 interface 然后判断类型,但是感觉略麻烦。想着是不是 go 的范型可以解决,但是目前看好像不太行😅

如果都是数字的话,声明成 float 就行?或者用 json.Number

如果还包含其他类型,那么相比之下,直接用 interface 来解 json 更方便了吧,用哪个字段判断一下类型。

下面是一个结构的定义。帖子不能修改了,把定义放到这里了。

go<br><br>type (<br> MetadataType interface {<br> ~string | ~int | ~float64 | ~bool<br> }<br><br> Embedding interface {<br> ~float64 | ~int<br> }<br>)<br><br>type Metadata[M MetadataType] map[string]M<br><br>type GetResult[M MetadataType, E Embedding] struct {<br> IDs []string `json:"ids"`<br> Embeddings []E `json:"embeddings,omitempty"`<br> Documents []string `json:"documents,omitempty"`<br> Metadatas []Metadata[M] `json:"metadatas,omitempty"`<br>}<br>

前段时间写了个 bencode 编解码库,.torrent 也是,tracker 字段可能是数组或者字符串,最后用 interface 解决。

那我也还是先用 interface 吧。

golang 非常好用的 JSON 解析库,可以直接获取单个值: https://github.com/tidwall/gjson

感谢,我试试😁

先变成 map interface ,然后判断字段,再转成对应结构体

https://github.com/mitchellh/mapstructure

https://github.com/json-iterator/go

jsoniter.Get([]byte({"a": 123,"b": {"c": "cc"}}),“b”).Get(“c”).ToString()

这种可变结果或者类型的 json 就不应该做成结构体啊,用可以动态获取的库,我觉得满屏的 interface 和在 ts 中满屏的 any 一样无法令人接受

还不如用 float64 接收,兼容 int float

用 json.Number ,尤其是会遇到大整数的场景

用 float64 的坑是遇到大整数,即使整数在 int64 范围内也会有精度损失问题
用 interface{} 也有问题,默认数字类型就会 decode 成 float64 ,除非 decoder 上用.UseNumber() 强制解析到 json.Number 类型

标准库可以用 json.RawMessage
第三方库随意哈

Json 的数据类型是基于 js 的,所以数字类型只有 float64 ,不存在其他类型。

可以定义一个 struct 来代表不同的类型,然后为这个 struct 实现 Marshal/ Unmarshal 。这样这个 struct 就能代表不同的类型,使用的时候也很方便。

比如 kubernetes 里就有类似的实现。https://pkg.go.dev/k8s.io/apimachinery/pkg/util/intstr#IntOrString

The core issue is how to convert float64 to int.
All the numbers in a JSON string are the type of float64.

I provide a suggestion.
golang<br>type Res struct {<br> Value any `json:"value"` // Int or Float<br> FloatValue float64<br> IntValue int<br>}<br><br>func (r *Res) UnmarshalJSON(source []byte) (err error) {<br> // json.number is type of float64<br> type Temp struct {<br> Value float64 `json:"value"`<br> }<br> var temp = Temp{}<br> decoder := json.NewDecoder(bytes.NewReader(source))<br> //means that number convert to json.Number<br> decoder.UseNumber()<br> //decode json<br> if err = decoder.Decode(&amp;temp); err != nil {<br> return err<br> }<br> var convertToInt bool<br> var convertValue int<br> // convert float64 to int<br> if convertToInt {<br> r.IntValue = convertValue<br> } else {<br> r.FloatValue = temp.Value<br> }<br> return<br>}<br>
usage:
golang<br>func Usage() {<br> var source = []byte(`{"value":123}`)<br> var res Res<br> _ = json.Unmarshal(source, &amp;res)<br> // use intValue<br> res.IntValue<br> // use floatValue<br> source = []byte(`{"value":123.23}`)<br> _ = json.Unmarshal(source, &amp;res)<br> res.FloatValue<br>}<br>


JSON 标准( RFC 8259 )对数字精度是没有限制的,可由具体实现自定义;只是说 double 类型用得更广泛,所以使用 double 会有更好的互操作性。

“如果还包含其他类型”

-------

这种情况不应该找对方打一架吗?

gjson/sjson

不折腾,用 float64

在Golang中处理JSON时,如果一个字段可能是多种类型,你可以使用空接口(interface{})来接收该字段,然后在解析后根据具体的类型进行处理。以下是一个示例:

  1. 首先,定义一个结构体,其中目标字段的类型为interface{}
type MyStruct struct {
    VarField interface{} `json:"var_field"`
}
  1. 然后,解析JSON数据到该结构体中。
data := []byte(`{"var_field": "string_value"}`) // 也可以是 {"var_field": 123} 或 {"var_field": [1, 2, 3]}
var result MyStruct
err := json.Unmarshal(data, &result)
if err != nil {
    log.Fatal(err)
}
  1. 接下来,根据VarField的实际类型进行处理。
switch v := result.VarField.(type) {
case string:
    fmt.Println("String:", v)
case int:
    fmt.Println("Integer:", v)
case []interface{}:
    fmt.Println("Array:", v)
default:
    fmt.Println("Unknown type")
}

使用type switch语句,你可以根据VarField的实际类型执行不同的逻辑。这种方法非常灵活,可以处理JSON中字段类型不确定的情况。

需要注意的是,如果JSON字段类型非常复杂或多样,可能需要更复杂的类型检查和错误处理逻辑。在实际应用中,确保对可能的类型进行了全面的覆盖,并妥善处理了所有可能的错误情况。

回到顶部