Golang 常见数据格式处理
数据格式介绍
- 数据格式是系统中数据交互不可缺少的内容
- 这里主要介绍
JSON
、XML
、MSGPack
JSON
- json 是完全独立于语言的文本格式,是 k-v 的形式 name:zs
- 应用场景:前后端交互,系统间数据交互
- json 使用 go 语言内置的 encoding/json 标准库
编码 json 使用 json.Marshal()
函数可以对一组数据进行 JSON 格式的编码
生成 json 格式
通过结构体生成 JSON
需要格式化的结构体中的字段必须是一个外部可调用的字段(首写字母大写),否则再 json 包中无法识别则读取不到
输出的 jsonkey
是字段名称
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string
Age int
}
func main() {
p := &Person{"zs", 18}
// 生成json
b, err := json.Marshal(p)
if err != nil {
fmt.Println("json 序列化失败", err)
return
}
fmt.Println(string(b))
// 格式化输出
b, err = json.MarshalIndent(p, "", " ")
if err != nil {
fmt.Println("json 格式化失败", err)
return
}
fmt.Println(string(b))
}
struct tag
上面我们可以直接将结构体序列化为一个 json,但是发现 json 的key
是字段名称,不是我们想要的key
名称,这个怎么办呢?这里就需要用到 go 中的tag
特性了,我们再字段后添加一个json
tag 指定想要输出的key
名称即可。
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
p := &Person{"zs", 18}
// 生成json
b, err := json.Marshal(p)
if err != nil {
fmt.Println("json 序列化失败", err)
return
}
fmt.Println(string(b))
}
通过 map 生成 json
package main
import (
"encoding/json"
"fmt"
)
func main() {
m := make(map[string]interface{})
m["name"] = "li"
m["age"] = 18
m["sex"] = "男"
b,err := json.Marshal(m)
if err != nil {
fmt.Println("json 序列化失败", err)
return
}
fmt.Println(string(b))
}
解析 json 格式
解码 json 使用json.Unmarshal()
函数可以对一组数据进行 JSON 格式的解码
func Unmarshal(data []byte, v interface{}) error
解析到结构体
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
//Age int `json:"int"`
Age int `json:"int,string"`
Sex string `json:"sex"`
}
func main() {
//jsonData := []byte(`{"name":"zs","age":18,"sex":"男"}`)
jsonData := []byte(`{"name":"zs","age":"18"","sex":"男"}`)
var p Person
err := json.Unmarshal(jsonData, &p)
if err != nil {
fmt.Println("反序列化失败", err)
return
}
fmt.Println(p)
}
解析到 interface
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"int"`
Sex string `json:"sex"`
}
func main() {
jsonData := []byte(`{"name":"zs","age":18,"sex":"男"}`)
var i interface{}
err := json.Unmarshal(jsonData, &i)
if err != nil {
fmt.Println("反序列化失败", err)
return
}
fmt.Println(i)
}
XML
- xml 是可扩展标记语言,包含声明、根标签、子元素和属性
- 应用场景:配置文件以及 webService
xml 文件示例
<?xml version="1.0" encoding="UTF-8" ?>
<servers version="1">
<server>
<serverName>Shanghai_VPN</serverName>
<serverIP>127.0.0.1</serverIP>
</server>
<server>
<serverName>Beijing_VPN</serverName>
<serverIP>127.0.0.2</serverIP>
</server>
</servers>
解析和读取规则
golang 对 xml 的解析和读取是通过 stuct 和 refect 实现的,对于 struct 中的 tag 以什么方式对应到 xml 的元素上,golang 的文档中做了如下描述:
- 如果 struct 的一个字段是 string 或者[]byte 类型且它的 tag 含有",innerxml",Unmarshal 将会将此字段所对应的元素内所有内嵌的原始 xml 累加到此字段上
- 如果 struct 中有一个叫做 XMLName,且类型为 xml.Name 字段,那么在解析的时候就会保存这个 element 的名字到该字段
- 如果某个 struct 字段的 tag 定义中含有 XML 结构中 element 的名称,那么解析的时候就会把相应的 element 值赋值给该字段
- 如果某个 struct 字段的 tag 定义了中含有",attr",那么解析的时候就会将该结构所对应的 element 的与字段同名的属性的值赋值给该字段
- 如果某个 struct 字段的 tag 定义 型如"a>b>c",则解析的时候,会将 xml 结构 a 下面的 b 下面的 c 元素的值赋值给该字段
- 如果某个 struct 字段的 tag 定义了"-",那么不会为该字段解析匹配任何 xml 数据
- 如果 struct 字段后面的 tag 定义了",any",如果他的子元素在不满足其他的规则的时候就会匹配到这个字段
- 如果某个 XML 元素包含一条或者多条注释,那么这些注释将被累加到第一个 tag 含有",comments"的字段上,这个字段的类型可能是[]byte 或 string,如果没有这样的字段存在,那么注释将会被抛弃
读取 xml
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
)
// 抽取xml结构
type Server struct {
ServerName string `xml:"serverName"`
ServerIP string `xml:"serverIP"`
}
type Servers struct {
XMLName xml.Name `xml:"servers"`
Version xml.Attr `xml:"version,attr"`
Servers []Server `xml:"server"`
}
func main() {
data, err := ioutil.ReadFile("./test.xml")
if err != nil {
fmt.Println(err)
return
}
var servers Servers
err = xml.Unmarshal(data, &servers)
if err != nil {
fmt.Println(err)
return
}
//fmt.Printf("%#v", servers)
fmt.Printf("%v", servers)
}
生成 xml
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
)
// 抽取xml结构
type Server struct {
ServerName string `xml:"serverName"`
ServerIP string `xml:"serverIP"`
}
type Servers struct {
XMLName xml.Name `xml:"servers"`
Version xml.Attr `xml:"version,attr"`
Servers []Server `xml:"server"`
}
func main() {
data, err := ioutil.ReadFile("./test.xml")
if err != nil {
println(err)
return
}
var servers Servers
err = xml.Unmarshal(data, &servers)
if err != nil {
println(err)
return
}
//fmt.Printf("%#v", servers)
fmt.Printf("%v\n", servers)
// 将读取到的xml数据,通过xml.Marshal()方法再转换为xml格式
d, err := xml.Marshal(&servers)
if err != nil {
println(err)
return
}
fmt.Println(string(d))
}
MSGPack
- MSGPack 是二进制的 json,性能更快,更省空间
- 需要安装第三方包:
go get -u github.com/vmihailenco/msgpack
package main
import (
"fmt"
"github.com/vmihailenco/msgpack"
"io/ioutil"
"math/rand"
)
type Person struct {
Name string
Age int
Sex string
}
// 写入json
func writeJson(filename string) (err error) {
var persons []*Person
// 制作数据
for i := 0; i < 10; i++ {
p := &Person{
Name: fmt.Sprintf("name%d", i),
Age: rand.Intn(100),
Sex: "male",
}
persons = append(persons, p)
}
// 二进制json格式化
data, err := msgpack.Marshal(persons)
if err != nil {
return
}
err = ioutil.WriteFile(filename, data, 0666)
if err != nil {
return
}
return
}
// 读取二进制json数据
func readJson(filename string) (persons []*Person, err error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return
}
// 反序列化
err = msgpack.Unmarshal(data, &persons)
if err != nil {
return
}
return persons, err
}
func main() {
//err := writeJson("./persons.json")
//if err != nil {
// fmt.Println(err)
// return
//}
persons, err := readJson("./persons.json")
if err != nil {
fmt.Println(err)
return
}
for _, v := range persons {
fmt.Printf("%#v", v)
}
}