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

2023-08-30 22:13:37 +08:00
 stevenshuang

求助:

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

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

2571 次点击
所在节点    Go 编程语言
25 条回复
seers
2023-08-30 22:16:31 +08:00
interface 然后断言行吗
stevenshuang
2023-08-30 22:19:28 +08:00
@seers 这样是可以的,我开始也是用 interface 然后判断类型,但是感觉略麻烦。想着是不是 go 的范型可以解决,但是目前看好像不太行😅
virusdefender
2023-08-30 22:20:27 +08:00
如果都是数字的话,声明成 float 就行?或者用 json.Number
stevenshuang
2023-08-30 22:31:23 +08:00
@virusdefender 如果还包含其他类型,那么相比之下,直接用 interface 来解 json 更方便了吧,用哪个字段判断一下类型。
stevenshuang
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
2023-08-30 22:38:40 +08:00
前段时间写了个 bencode 编解码库,.torrent 也是,tracker 字段可能是数组或者字符串,最后用 interface 解决。
stevenshuang
2023-08-30 22:45:18 +08:00
@xlsepiphone 那我也还是先用 interface 吧。
morebuff
2023-08-30 23:05:27 +08:00
golang 非常好用的 JSON 解析库,可以直接获取单个值: https://github.com/tidwall/gjson
stevenshuang
2023-08-30 23:28:59 +08:00
@morebuff 感谢,我试试😁
iyaozhen
2023-08-30 23:33:17 +08:00
先变成 map interface ,然后判断字段,再转成对应结构体

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

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

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

比如 kubernetes 里就有类似的实现。https://pkg.go.dev/k8s.io/apimachinery/pkg/util/intstr#IntOrString
yianing
2023-08-31 10:27:07 +08:00
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
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
}
```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/969605

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX