郑州商务网站建设,响应式网页设计的目的是什么,邮箱域名可以做网站吗,做网站加入广告联盟golang 编码 json 还比较简单#xff0c;而解析 json 则非常蛋疼。不像 PHP 一句 json_decode() 就能搞定。之前项目开发中#xff0c;为了兼容不同客户端的需求#xff0c;请求的 content-type 可以是 json#xff0c;也可以是 www-x-urlencode。然后某天前端希望某个后端… golang 编码 json 还比较简单而解析 json 则非常蛋疼。不像 PHP 一句 json_decode() 就能搞定。之前项目开发中为了兼容不同客户端的需求请求的 content-type 可以是 json也可以是 www-x-urlencode。然后某天前端希望某个后端服务提供 json 的处理而当时后端使用 java 实现了 www-x-urlencode 的请求对于突然希望提供 json 处理产生了极大的情绪。当时不太理解现在看来对于静态语言解析未知的 JSON 确实是一项挑战。 定义结构 与编码 json 的 Marshal 类似解析 json 也提供了 Unmarshal 方法。对于解析 json也大致分两步首先定义结构然后调用 Unmarshal 方法序列化。我们先从简单的例子开始吧。 type Account struct {Email string json:emailPassword string json:passwordMoney float64 json:money
}var jsonString string {email: phpgo163.com,password : 123456,money : 100.5
}func main() {account : Account{}err : json.Unmarshal([]byte(jsonString), account)if err ! nil {log.Fatal(err)}fmt.Printf(%v\n, account)
} 输出 {Email:phpgo163.com Password:123456 Money:100.5}Unmarshal 接受一个 byte 数组和空接口指针的参数。和 sql 中读取数据类似先定义一个数据实例然后传其指针地址。 与编码类似golang 会将 json 的数据结构和 go 的数据结构进行匹配。匹配的原则就是寻找 tag 的相同的字段然后查找字段。查询的时候是 大小写不敏感的 type Account struct {Email string json:emailPassWord stringMoney float64 json:money
}var jsonString string {email: phpgo163.com,password : 123456,money : 100.5
}func main() {account : Account{}err : json.Unmarshal([]byte(jsonString), account)if err ! nil {log.Fatal(err)}fmt.Printf(%v\n, account)
} 输出 {Email:phpgo163.com PassWord:123456 Money:100.5} 把 Password 的 tag 去掉再修改成 PassWord依然可以把 json 的 password 匹配到 PassWord但是如果结构的字段是私有的即使 tag 符合也不会被解析 type Account struct {Email string json:emailpassword string json:passwordMoney float64 json:money
}var jsonString string {email: phpgo163.com,password : 123456,money : 100.5
}func main() {account : Account{}err : json.Unmarshal([]byte(jsonString), account)if err ! nil {log.Fatal(err)}fmt.Printf(%v\n, account)
} 输出 {Email:phpgo163.com password: Money:100.5}上面的 password 并不会被解析赋值 json 的 password大小写不敏感只是针对公有字段而言。 再寻找 tag 或字段的时候匹配不成功则会抛弃这个 json 字段的值 type Account struct {Email string json:emailPassword string json:password
}var jsonString string {email: phpgo163.com,password : 123456,money : 100.5
}func main() {account : Account{}err : json.Unmarshal([]byte(jsonString), account)if err ! nil {log.Fatal(err)}fmt.Printf(%v\n, account)
} 输出 {Email:phpgo163.com Password:123456} 并不会有money字段被赋值。 string tag 在编码的时候我们使用 tag string可以把结构定义的数字类型以字串形式编码。同样在解码的时候只有字串类型的数字才能被正确解析否则会报错 type Account struct {Email string json:emailPassword string json:passwordMoney float64 json:money,string
}var jsonString string {email: phpgo163.com,password : 123456,money : 100.5 // 不能没有 双引号否则会报错
}func main() {account : Account{}err : json.Unmarshal([]byte(jsonString), account)if err ! nil {log.Fatal(err)}fmt.Printf(%v\n, account)
} 输出 {Email:phpgo163.com Password:123456 Money:100.5} Money 是 float64 类型。 如果 json 的 money 是 100.5 会得到下面的错误 2017/03/08 17:39:21 json: invalid use of ,string struct tag, trying to unmarshal unquoted value into float64
exit status 1 - tag 与编码一样tag 的-也不会被解析但是会初始化其 零值 type Account struct {Email string json:emailPassword string json:passwordMoney float64 json:-
}var jsonString string {email: phpgo163.com,password : 123456,money : 100.5
}func main() {account : Account{}err : json.Unmarshal([]byte(jsonString), account)if err ! nil {log.Fatal(err)}fmt.Printf(%v\n, account)
} 输出 {Email:phpgo163.com Password:123456 Money:0} 稍微总结一下解析 json 最好的方式就是定义与将要被解析 json 的结构。有人写了一个小工具 json-to-go自动将 json 格式化成 golang 的结构。 动态解析 通常根据 json 的格式预先定义 golang 的结构进行解析是最理想的情况。可是实际开发中理想的情况往往都存在理想的愿望之中很多 json 非但格式不确定有的还可能是动态数据类型。 例如通常登录的时候往往既可以使用手机号做用户名也可以使用邮件做用户名客户端传的 json 可以是字串也可以是数字。此时服务端解析就需要技巧了。 Decode 前面我们使用了简单的方法 Unmarshal 直接解析 json 字串下面我们使用更底层的方法 NewDecode 和 Decode 方法。 package mainimport (encoding/jsonfmtiologstrings
)type User struct {UserName string json:usernamePassword string json:password
}var jsonString string {username: phpgo163.com,password: 123
}func Decode(r io.Reader) (u *User, err error) {u new(User)err json.NewDecoder(r).Decode(u)if err ! nil {return}return
}func main() {user, err : Decode(strings.NewReader(jsonString))if err ! nil {log.Fatal(err)}fmt.Printf(%#v\n, user)
}输出 main.User{UserName:phpgo163.com, Password:123} 我们定义了一个 Decode 函数在这个函数进行 json 字串的解析。然后调用 json 的 NewDecoder 方法构造一个 Decode 对象最后使用这个对象的 Decode 方法赋值给定义好的结构对象。 对于字串可是使用 strings.NewReader 方法让字串变成一个 Stream 对象。 接口 如果客户端传的 username 的值是一个数字类型的手机号那么上面的解析方法将会失败。正如我们之前所介绍的动态类型行为一样使用空接口可以 hold 住这样的情景。 type User struct {UserName interface{} json:usernamePassword string json:password
}var jsonString string {username: 15899758289,password: 123
}func Decode(r io.Reader) (u *User, err error) {u new(User)err json.NewDecoder(r).Decode(u)if err ! nil {return}return
}func main() {user, err : Decode(strings.NewReader(jsonString))if err ! nil {log.Fatal(err)}fmt.Printf(%#v\n, user)
} 输出 main.User{UserName:1.5899758289e10, Password:123} 貌似成功了可是返回的数字是科学计数法有点奇怪。可以使用 golang 的断言然后转换类型 type User struct {UserName interface{} json:usernamePassword string json:password
}var jsonString string {username: 15899758289,password: 123
}func Decode(r io.Reader) (u *User, err error) {u new(User)if err json.NewDecoder(r).Decode(u); err ! nil {return}switch t : u.UserName.(type) {case string:u.UserName tcase float64:u.UserName int64(t)}return
}func main() {user, err : Decode(strings.NewReader(jsonString))if err ! nil {log.Fatal(err)}fmt.Printf(%#v\n, user)
} 输出 main.User{UserName:15899758289, Password:123} 看起来挺好可是我们的 UserName 字段始终是一个空接口使用他的时候还是需要转换类型这样情况看来解析的时候就应该转换好类型那么用的时候就省心了。 修改定义的结构如下 type User struct {UserName interface{} json:usernamePassword string json:passwordEmail stringPhone int64
} 这样就能通过 fmt.Println(user.Email add me) 使用字段进行操作了。当然也有人认为 Email 和 Phone 纯粹多于因为使用的时候还是需要再判断当前结构实例是那种情况。 延迟解析 因为 UserName 字段实际上是在使用的时候才会用到他的具体类型因此我们可以延迟解析。使用 json.RawMessage 方式将 json 的字串继续以 byte 数组方式存在。 type User struct {UserName json.RawMessage json:usernamePassword string json:passwordEmail stringPhone int64
}var jsonString string {username: phpgo163.com,password: 123
}func Decode(r io.Reader) (u *User, err error) {u new(User)if err json.NewDecoder(r).Decode(u); err ! nil {return}var email stringif err json.Unmarshal(u.UserName, email); err nil {u.Email emailreturn}var phone int64if err json.Unmarshal(u.UserName, phone); err nil {u.Phone phone}return
}func main() {user, err : Decode(strings.NewReader(jsonString))if err ! nil {log.Fatal(err)}fmt.Printf(%#v\n, user)
} 总体而言延迟解析和使用空接口的方式类似。需要再次调用 Unmarshal 方法对 json.RawMessage 进行解析。原理和解析到接口的形式类似。 不定字段解析 对于未知 json 结构的解析不同的数据类型可以映射到接口或者使用延迟解析。有时候会遇到 json 的数据字段都不一样的情况。例如需要解析下面一个 json 字串 接口配合断言 var jsonString string {things: [{name: Alice,age: 37},{city: Ipoh,country: Malaysia},{name: Bob,age: 36},{city: Northampton,country: England}]} json 字串的是一个对象其中一个 key things 的值是一个数组这个数组的每一个 item 都未必一样大致是两种数据结构可以抽象为 person 和 place。即定义下面的结构体 type Person struct {Name string json:nameAge int json:age
}type Place struct {City string json:cityCountry string json:country
} 接下来我们 Unmarshal json 字串到一个 map 结构然后迭代 item 并使用 type 断言的方式解析数据 func decode(jsonStr []byte) (persons []Person, places []Place) {var data map[string][]map[string]interface{}err : json.Unmarshal(jsonStr, data)if err ! nil {fmt.Println(err)return}for i : range data[things] {item : data[things][i]if item[name] ! nil {persons addPerson(persons, item)} else {places addPlace(places, item)}}return
} 迭代的时候会判断 item 是否是 person 还是 place然后调用对应的解析方法 func addPerson(persons []Person, item map[string]interface{}) []Person {name : item[name].(string)age : item[age].(float64)person : Person{name, int(age)}persons append(persons, person)return persons
}func addPlace(places []Place, item map[string]interface{}) []Place {city : item[city].(string)country : item[country].(string)place : Place{City: city, Country: country}places append(places, place)return places
} 代码汇总 type Person struct {Name string json:nameAge int json:age
}type Place struct {City string json:cityCountry string json:country
}func decode(jsonStr []byte) (persons []Person, places []Place) {var data map[string][]map[string]interface{}err : json.Unmarshal(jsonStr, data)if err ! nil {fmt.Println(err)return}for i : range data[things] {item : data[things][i]if item[name] ! nil {persons addPerson(persons, item)} else {places addPlace(places, item)}}return
}func addPerson(persons []Person, item map[string]interface{}) []Person {name : item[name].(string)age : item[age].(float64)person : Person{name, int(age)}persons append(persons, person)return persons
}func addPlace(places []Place, item map[string]interface{}) []Place {city : item[city].(string)country : item[country].(string)place : Place{City: city, Country: country}places append(places, place)return places
}var jsonString string {things: [{name: Alice,age: 37},{city: Ipoh,country: Malaysia},{name: Bob,age: 36},{city: Northampton,country: England}]
}func main() {personA, placeA : decode([]byte(jsonString))fmt.Printf(%v\n, personA)fmt.Printf(%v\n, placeA)
}输出 [{Name:Alice Age:37} {Name:Bob Age:36}]
[{City:Ipoh Country:Malaysia} {City:Northampton Country:England}] 混合结构 混合结构很好理解如同我们前面解析 username 为 email 和 phone 两种情况就在结构中定义好这两种结构即可。 type Mixed struct {Name string json:nameAge int json:agecity string json:cityCountry string json:country
} 混合结构的思路很简单借助 golang 会初始化没有匹配的 json 和抛弃没有匹配的 json给特定的字段赋值。比如每一个 item 都具有四个字段只不过有的会匹配 person 的 json 数据有的则是匹配 place。没有匹配的字段则是零值。接下来在根据 item 的具体情况分别赋值到对于的 Person 或 Place 结构。 type Person struct {Name string json:nameAge int json:age
}type Place struct {City string json:cityCountry string json:country
}type Mixed struct {Name string json:nameAge int json:agecity string json:cityCountry string json:country
}func decode(jsonStr []byte) (persons []Person, places []Place) {var data map[string][]Mixederr : json.Unmarshal(jsonStr, data)if err ! nil {fmt.Println(err)return}fmt.Printf(%v\n, data[things])for i : range data[things] {item : data[things][i]if item.Name ! {persons append(persons, Person{Name: item.Name, Age: item.Age})} else {places append(places, Place{City: item.city, Country:item.Country})}}return
}var jsonString string {things: [{name: Alice,age: 37},{city: Ipoh,country: Malaysia},{name: Bob,age: 36},{city: Northampton,country: England}]
}func main() {personA, placeA : decode([]byte(jsonString))fmt.Printf(%v\n, personA)fmt.Printf(%v\n, placeA)
}输出 [{Name:Alice Age:37 city: Country:} {Name: Age:0 city: Country:Malaysia} {Name:Bob Age:36 city: Country:} {Name: Age:0 city: Country:England}]
[{Name:Alice Age:37} {Name:Bob Age:36}]
[{City: Country:Malaysia} {City: Country:England}] 混合结构的解析方式也很不错。思路还是借助了解析 json 中抛弃不要的字段借助零值处理。 json.RawMessage json.RawMessage 非常有用延迟解析也可以使用这个样例。我们已经介绍过类似的技巧下面就贴代码了 type Person struct {Name string json:nameAge int json:age
}type Place struct {City string json:cityCountry string json:country
}func addPerson(item json.RawMessage, persons []Person) ([]Person) {person : Person{}if err : json.Unmarshal(item, person); err ! nil {fmt.Println(err)} else {if person ! *new(Person) {persons append(persons, person)}}return persons
}func addPlace(item json.RawMessage, places []Place) ([]Place) {place : Place{}if err : json.Unmarshal(item, place); err ! nil {fmt.Println(err)} else {if place ! *new(Place) {places append(places, place)}}return places
}func decode(jsonStr []byte) (persons []Person, places []Place) {var data map[string][]json.RawMessageerr : json.Unmarshal(jsonStr, data)if err ! nil {fmt.Println(err)return}for _, item : range data[things] {persons addPerson(item, persons)places addPlace(item, places)}return
}var jsonString string {things: [{name: Alice,age: 37},{city: Ipoh,country: Malaysia},{name: Bob,age: 36},{city: Northampton,country: England}]
}func main() {personA, placeA : decode([]byte(jsonString))fmt.Printf(%v\n, personA)fmt.Printf(%v\n, placeA)
} 输出 [{Name:Alice Age:37} {Name:Bob Age:36}]
[{City:Ipoh Country:Malaysia} {City:Northampton Country:England}] 把 things 的 item 数组解析成一个 json.RawMessage然后再定义其他结构逐步解析。上述这些例子其实在真实的开发环境下应该尽量避免。像 person 或是 place 这样的数据可以定义两个数组分别存储他们这样就方便很多。不管怎么样通过这个略傻的例子我们也知道了如何解析 json 数据。 总结 关于 golang 解析 json 的介绍基本就这么多。想要解析越简单就需要定义越明确的 map 结构。面对无法确定的数据结构或类型再动态解析方面可以借助接口与断言的方式解析也可以使 json.RawMessage 延迟解析。具体使用情况还得考虑实际的需求和应用场景。 总而言之使用 json 作为现在 api 的数据通信方式已经很普遍了。 相关文章 Golang 处理 Json一编码 Golang 处理 Json二解码 参考 http://json.org/ http://www.jianshu.com/p/31757e530144 https://golang.org/pkg/encoding/json/ 转载于:https://www.cnblogs.com/52php/p/6518728.html