引言
这个问题出现在解析一个json的配置文件时,排错了很久,最后和狗勋一起解决了这个奇怪的问题。
正文
其实这个问题并不难,因为Golang自带json包,所以我们要做的其实就是在需要解析的结构体中加上相应的标签就可以了,但是可能会出现一些隐性的bug,且没有任何的报错。
我一共遇到了两种问题,其中第一种比较好解决,网上的解决方案也大多是描述第一种问题;但是第二种问题就非常的蹊跷了,因为在项目中出现了问题,写了一个测试代码并没有复现成功。
第一种解决方案:结构体需要解析的相应字段应该大写。
我们先来看一段代码:
---test.json
{
"page": 1,
"fruits": ["apple", "peach"]
}
---test_json.go
type response struct {
number int
Page int `json:page`
fruits []string `json:fruits`
}
func main(){
data, _ := io.ReadFile("test.json")
res := response{
}
json.Unmarshal(data, &res)
fmt.Println(res)
}
ouput:
{
0 1 []}
我们可以看到在首字母小写时出现问题,解决方案也很容易,就是需要把字段开头改成大写,当然这样是有弊端的,因为一般需要从配置文件中读取的字段一般都是与配置相关的,而这些一般应该设置成private的,C++就可以很好的解决,这也许就是go不如C++自由的原因吧,当然这可能也是C++没有类似go这样json库的原因吧。
第二种解决方案:就是Goland的json解析要么加双引号,要么不加双引号名称中不能带下划线,即_
。
也就是上面的结构体应该写成这样:
type response struct {
number int
Page int `json:"page"`
fruits []string `json:"fruits"`
}
也就是这样子,虽然这个测试代码不加双引号也可以正确解析出slice,但是在项目中的结构体却出现了问题,我把项目中的结构体中带json字段的提取出来就可以复现错误了:
---test.json
{
"servers_address": ["localhost:8901","localhost:8902"],
"myport" : ":8900",
"maxreries" : 13,
"timeout_entry" : 200
}
---test_json.go
type response struct {
Maxreries int `json:maxreries`
ServersAddress []string `json:servers_address`
MyPort string `json:myport`
TimeOutEntry int `json:timeout_entry`
}
func main(){
data, _ := io.ReadFile("test.json")
res := response{
}
json.Unmarshal(data, &res)
fmt.Println(res)
}
ouput:
{
13 [] :8900 0}
我们可以看到解析失败,ServersAddress
和TimeOutEntry
解析失败,这已经不是解析slice失败了,int也出现问题,让我们加上双引号看看:
ouput:
{
13 [localhost:8901 localhost:8902] :8900 200}
我们可以看到解析成功。
那么不加双引号为什么有时可以解析成功,有时不可以呢?因为已经不是解析slice失败了,而解析int也同样出现了错误。
观察两个出现问题的字段,发现它们有一个共同点,就是json标签中都带了_
,让我们去掉执行看看:
{
13 [localhost:8901 localhost:8902] :8900 200}
排查出错误!就是Goland的json解析在结构体json标签不加双引号时无法解析名称带_
的字段!
这个问题估计是一个极其隐式的问题,官方文档中也没有描述,网上在此之前也没有一篇文章描述这个问题,希望看完这篇文章能帮助你解决这个令人困惑不以的问题。
参考:
- 博文《Go by Example: JSON》
- 文档《func Unmarshal》