Golang 常见数据格式处理

数据格式介绍

  • 数据格式是系统中数据交互不可缺少的内容
  • 这里主要介绍JSONXMLMSGPack

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特性了,我们再字段后添加一个jsontag 指定想要输出的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 的文档中做了如下描述:

  1. 如果 struct 的一个字段是 string 或者[]byte 类型且它的 tag 含有",innerxml",Unmarshal 将会将此字段所对应的元素内所有内嵌的原始 xml 累加到此字段上
  2. 如果 struct 中有一个叫做 XMLName,且类型为 xml.Name 字段,那么在解析的时候就会保存这个 element 的名字到该字段
  3. 如果某个 struct 字段的 tag 定义中含有 XML 结构中 element 的名称,那么解析的时候就会把相应的 element 值赋值给该字段
  4. 如果某个 struct 字段的 tag 定义了中含有",attr",那么解析的时候就会将该结构所对应的 element 的与字段同名的属性的值赋值给该字段
  5. 如果某个 struct 字段的 tag 定义 型如"a>b>c",则解析的时候,会将 xml 结构 a 下面的 b 下面的 c 元素的值赋值给该字段
  6. 如果某个 struct 字段的 tag 定义了"-",那么不会为该字段解析匹配任何 xml 数据
  7. 如果 struct 字段后面的 tag 定义了",any",如果他的子元素在不满足其他的规则的时候就会匹配到这个字段
  8. 如果某个 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)
	}
}
posted @ 2020-03-17 11:30  ZhiChao&  阅读(1127)  评论(0编辑  收藏  举报