当前位置: 首页 > 科技观察

在Go中简化JSON处理

时间:2023-03-23 12:10:01 科技观察

我的第一个Go项目需要处理一堆JSON测试装置并将JSON数据作为参数传递给我们构建的API以进行处理。另一个团队创建了这些测试装置来为API提供独立于语言、可预测的输入和输出。在强类型语言中,JSON通常很难处理——JSON类型有字符串、数字、字典和数组。如果你的语言是javascript、python、ruby或PHP,JSON有一个很大的好处就是你在解析和编码数据时不需要考虑类型。//在PHP$object=json_decode('{"foo":"bar"}');//在javascript中constobject=JSON.parse('{"foo":"bar"}')在强类型语言中,您需要自己定义如何处理字符串、数字、字典和JSON对象数组。在Go语言中,当你使用内置的API时,你需要考虑如何更好地将一个JSON文件转换成Go数据结构。我不打算深入探讨如何在Go中处理JSON的复杂主题,我将仅列出两个代码示例来说明这个问题。源码详见Go实例教程[1]parse/serializetomap[string]interface首先来看这个程序。packagemainimport("encoding/json""fmt")funcmain(){byte:=[]byte(`{"num":6.13,"strs":["a","b"],"obj":{"foo":{"bar":"zip","zap":6}}}`)vardatmap[string]interface{}iferr:=json.Unmarshal(byt,&dat);err!=nil{panic(err)}fmt.Println(dat)num:=dat["num"].(float64)fmt.Println(num)strs:=dat["strs"].([]interface{})str1:=strs[0].(string)fmt.Println(str1)obj:=dat["obj"].(map[string]interface{})obj2:=obj["foo"].(map[string]interface{})fmt.Println(obj2)}我们将JSON数据从byt变量反序列化(解析、解码等)到名为dat的映射/字典对象中。这些操作与其他语言类似,区别在于我们的输入需要是一个字节数组(而不是字符串),并且对于字典的每个值,都需要一个类型断言[2]来读取或访问该值。当我们处理一个多层嵌套的JSON对象时,这些类型断言会使处理过程变得非常繁琐。解析/序列化为struct第二种处理如下:[string]map[string]string`json:"obj"`}funcmain(){byte:=[]byte(`{"num":6.13,"strs":["a","b"],"obj":{"foo":{"bar":"zip","zap":6}}}`)res:=ourData{}json.Unmarshal(byt,&res)fmt.Println(res.Num)fmt.Println(res.Strs)fmt.Println(res.Obj)}我们使用Gostruct的label函数将byt变量中的字节反序列化为具体的结构体ourData。标签是结构成员定义之后的字符串。我们的定义如下:typeourDatastruct{Numfloat64`json:"num"`Strs[]string`json:"strs"`Objmap[string]map[string]string`json:"obj"`}就可以了参见Num成员的JSON标签“num”、Str成员的JSON标签“strs”和Obj成员的JSON标签“obj”。这些字符串使用反引号[3]将标签声明为文字字符串。除了反引号,您还可以使用双引号,但使用双引号可能需要一些额外的转义,这看起来很乱。在结构中输入ourDatastruct{Numfloat64"json:\"num\""Strs[]string"json:\"strs\""Objmap[string]map[string]string"json:\"obj\""}在的定义中,标签不是必需的。如果你的结构中有一个标签,这意味着Go的反射API[4]可以访问标签的值[5]。Go中的包可以使用这些标签来执行某些操作。Go的encoding/json包在将JSON成员反序列化为具体结构时使用这些标签来确定每个顶级JSON成员的值。换句话说,当你这样定义一个结构时:typeourDatastruct{Numfloat64`json:"num"`}意思是:当使用json.Unmarshal将一个JSON对象反序列化到这个结构中时,取它的顶级num成员值并将其分配给该结构的Num成员。这个操作可以让你的反序列化代码稍微干净一些,因为程序员不需要为每个成员值显式调用类型断言。但是,这仍然不是最好的解决方案。首先——标签只对顶级成员有效——嵌套的JSON需要对应嵌套的类型(比如Objmap[string]map[string]string),所以还是避免不了繁琐的操作。其次-它假定您的JSON结构不会改变。如果你运行上面的程序,你会发现“zap”:6并没有赋值给Obj成员。您可以通过创建类型map[string]map[string]interface{}来处理此问题,但在这里您需要再次进行类型断言。这是我的第一个Go项目,它让我很痛苦。幸运的是,现在我们有了更有效的方法。SJSON和GJSONGo内置的JSON处理没有改变,但是已经出现了一些成熟的包,旨在对JSON处理更加简洁和高效。SJSON[6](写入JSON)和GJSON[7](读取JSON)是由JoshBaker[8]开发的两个包,您可以使用它们来读取和写入JSON字符串。您可以参考README中的代码示例——这里是一个使用GJSON从JSON字符串中获取嵌套值的示例:packagemainimport"github.com/tidwall/gjson"constJSON=`{"name":{"first":"Janet","last":"Prichard"},"age":47}`funcmain(){value:=gjson.Get(json,"name.last")println(value.String())}同样,下面是一个示例代码,它使用SJSON"set"JSON字符串中的值返回设置字符串:packagemainimport"github.com/tidwall/sjson"constJSON=`{"name":{"first":"Janet","last":"Prichard"},"age":47}`funcmain(){value,_:=sjson.Set(json,"name.last","Anderson")println(value)}如果SJSON和GJSON不合您的口味,还有[9]其他[10]第三方库[11]可用于在Go程序中处理稍微复杂一点的JSON。