數據格式介紹
- 數據格式是系統中數據交互不可缺少的內容
- 這里主要介紹
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)
}
}