使用 Golang 的 json 解析库遇到的一点问题

2019-05-30 17:37:15 +08:00
 whoami9894

一个请求 ISBN 查询 API 的服务,查询信息的字段是:

type info struct {
	ISBN       string `json:"isbn"`
	Title      string `json:"title"`
	Subtitle   string `json:"subtitle"`
	Pic        string `json:"pic"`
	Author     string `json:"author"`
	Summary    string `json:"summary"`
	Publisher  string `json:"publisher"`
	Pubplace   string `json:"pubplace"`
	Page       int    `json:"page"`
	Price      string `json:"price"`
	Binding    string `json:"binding"`
	Keyword    string `json:"keyword"`
	Edition    string `json:"edition"`
	Impression string `json:"impression"`
	Language   string `json:"language"`
	Format     string `json:"format"`
	Class      string `json:"class"`
}

这里的 page 最初我写了 string 类型,然后解析报错了json: cannot unmarshal number into Go value of type string

然后我改为了 int 类型,然后依然会时不时报错json: cannot unmarshal string into Go struct field info.page of type int???昨天报错了几次,之后却一直正常想复现都不行,今天又报错了。查验了那个 ISBN API 应该没有问题,给的 page 就是 integer

但是这里报错后却插入数据库了,逻辑是这样的:

func get(isbn string) (*info, error) {
	url := baseURL + isbn
	resp, err := nic.Get(url, nil)
	if err != nil {
		return nil, err
	}

	t := &tmp{0, "", &info{}}
	err = resp.JSON(t)   //***************************************** error here
	if err != nil {
		return nil, err
	}
	if t.Status != 0 {
		return nil, errors.New(t.Msg)
	}

	return t.Result, nil
}

func query(c *mgo.Collection, isbn string) (*info, error) {
	data := &info{}
	c.Find(bson.M{"isbn": isbn}).One(data)

	if data.ISBN == "" {
		var err error
		data, err = get(isbn)
		if err != nil {
			return nil, err
		}
		err = c.Insert(data)
		if err != nil {
			return nil, err
		}
	}
	return data, nil
}

而且第一次报错后插入数据库的数据却是正确的,page 就是正确的 integer:

 {'isbn': '9787212053937', 'page': 236, 'price': '21.50', 'binding': '', 'keyword': '', 'edition': '', 'impression': '', 'language': '', 'format': '', 'class': ''}

orz

9428 次点击
所在节点    Go 编程语言
16 条回复
whoami9894
2019-05-30 17:42:13 +08:00
我知道了,貌似是 mgo 报的错
wangsongyan
2019-05-30 18:44:08 +08:00
如果是因为字段类型不一致,可以试试 json.Number
petelin
2019-05-30 18:56:20 +08:00
估计有 number 有 string
whoami9894
2019-05-30 19:13:00 +08:00
@wangsongyan
@petelin

感谢回复,我又测试了一下,果然这个 zz 的 API 第一次请求返回 int 之后都返回 string,服了......

```
2019/05/30 19:08:29 int
2019/05/30 19:08:29 {"status":0,"msg":"ok","result":{"title":"课本全解( 5 年级上)","
author":null,"subtitle":null,"pubdate":" 2012-7","page":" 236","price":" 21.50 元","s ummary":"","pic":"http:\/\/api.jisuapi.com\/isbn\/\/upload\/3663\/3662449.","isbn":"9787212053928","isbn10":"7212053929","sellerlist":[{"seller":"douban","price":"21.50"}]}}
2019/05/30 19:08:29 89
2019/05/30 19:08:29 json: cannot unmarshal string into Go struct field info.page of type int

[GIN] 2019/05/30 - 19:08:29 | 200 | 1.5377224s | 127.0.0.1 | POST /api/v1/isbn-query
2019/05/30 19:10:06 int
2019/05/30 19:10:06 {"status":0,"msg":"ok","result":{"title":"课本全解( 5 年级上)","
subtitle":null,"pic":"http:\/\/api.jisuapi.com\/isbn\/upload\/3663\/3662449.","author":null,"summary":null,"publisher":null,"pubplace":null,"pubdate":" 2012-7","page":236,"price":"21.50","binding":null,"isbn":"9787212053928","isbn10":"7212053929","keyword":null,"edition":null,"impression":null,"language":null,"format":null,"class":null,"sellerlist":[{"seller":"douban","price":"21.50"}]}}
[GIN] 2019/05/30 - 19:10:06 | 200 | 176.5282ms | 127.0.0.1 | POST /api/v1/isbn-query
[GIN] 2019/05/30 - 19:10:16 | 200 | 0s | 127.0.0.1 | POST /api/v1/isbn-query
```
Trim21
2019-05-30 19:30:25 +08:00
初学 golang,正好看到这个问题,请教下,这样类型不确定的 json 要怎么解析?
whoami9894
2019-05-30 19:45:39 +08:00
@wangsongyan
@petelin
json.Number 有点问题,page 字段可能返回空`"page": null,`,用 json.Number 就报错了`failed to convert json.Number to a number:`,改成了 interface{}暂时测试没什么问题
whoami9894
2019-05-30 19:47:06 +08:00
@Trim21
我这里把 page 定义成空接口类型了,假如之后需要这个确定类型的话再做类型断言就行
Reficul
2019-05-30 20:23:28 +08:00
对于要兼容这种奇葩的字段,可以这样:

type Number int64

然后给这个 Number 实现 json.Unmarshaler 接口来自定义解析逻辑。
whoami9894
2019-05-30 20:45:57 +08:00
@Reficul 感谢回复,学到了
donething
2019-05-30 22:24:32 +08:00
@Reficul 感谢,这个方法好。
Allianzcortex
2019-05-30 22:42:35 +08:00
@Reficul 厉害
JimmyTinsley
2019-05-31 08:59:16 +08:00
我倒是挺好奇这个 api 是怎么实现一会儿返回 int 一会儿返回 string 的...
wweir
2019-05-31 10:37:44 +08:00
@liujie333333 动态类型语言比较容易出现这样的情况,当然,还是看使用者的水平
janxin
2019-05-31 11:21:53 +08:00
@Trim21 最理想是 interface{},什么都能收,然后根据具体类型做处理
whoami9894
2019-05-31 11:55:10 +08:00
@liujie333333
最逗的是这个 API 文档告诉我类型是 string,然后第一次请求返回 string 之后请求全是 int,可能跟服务端某些缓存机制有关
lxz6597863
2019-06-03 09:10:26 +08:00
https://github.com/json-iterator/go 这个包有个 Any 类型,可以试下

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

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

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

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

© 2021 V2EX