json的数据类型
Go | json |
---|---|
bool | bool |
浮点数/整数 | 常规数字 |
string(utf-8) | string(Unicode) |
array/slice | array |
[]byte | string(Base64) |
struct | JSON对象 |
map[string]T | JSON对象 |
1、 marshal空接口:interface{}类型其实是个空接口,即没有方法的接口。go的每一种类型都实现了该接口。因此,任何其他类型的数据都可以赋值给interface{}类型。无论是string,int,bool,还是指针类型等,都可赋值给interface{}类型,在marshal序列化的时候且正常编码。
type Stu struct {
Name interface{} `json:"name"`
Age interface{}
HIgh interface{}
sex interface{}
Class interface{} `json:"class"`
}
type Class struct {
Name string
Grade int
}
//实例化一个数据结构,用于生成json字符串
stu := Stu{
Name: "张三",
Age: 18,
HIgh: true,
sex: "男",
}
cla := new(Class)
cla.Name = "1班"
cla.Grade = 3
stu.Class=cla
//Marshal失败时err!=nil
jsonStu, err := json.Marshal(stu)
if err != nil {
fmt.Println("生成json字符串错误")
}
//jsonStu是[]byte类型,转化成string类型便于查看
fmt.Println(string(jsonStu))
//输出 {"name":"张三","Age":18,"HIgh":true,"class":{"Name":"1班","Grade":3}}
2、 unMarshal空接口: 对于复合数据,在unmarshal的时候,如果接收体中配的项被声明为interface{}类型,go都会默认解析成map[string]interface{}类型。如果我们想直接解析到struct Class对象中,可以将接受体对应的项定义为该struct类型。
type httpResp struct {
RetCode int `json:"ret_code"`
Data interface{} `json:"data"`
Message string `json:"message"`
}
//若response中的字段Data为复合数据,则response的data字段经过unMarshal后会变成map[string]interface{}的type形式。
json.Unmarshal([]byte(response), &httpRsp)
json的序列化
1、Marshal方法 :用于将struct、map、slice序列化为json数据。
定义结构体的时候,只有字段名是大写的,才会被编码到json当中。
//将struct对象序列化成json,将map序列化成json将slice序列化成json
func Marshal(v interface{}) ([]byte, error) {
e := newEncodeState()
err := e.marshal(v, encOpts{escapeHTML: true})
if err != nil {
return nil, err
}
buf := append([]byte(nil), e.Bytes()...)
e.Reset()
encodeStatePool.Put(e)
return buf, nil
}
标签: 每个结构字段的编码可以通过结构字段标签中“json”键下存储的格式字符串来定制。格式字符串给出字段的名称,可能后跟逗号分隔的选项列表。名称可能为空,以指定选项而不覆盖默认字段名称。
-
“omitempty”选项指定如果字段具有空值,定义为false,0,零指针,nil接口值以及任何空数组,切片,映射或字符串,则该字段应从编码中省略。
Addr string
json:"addr,omitempty"
-
作为特殊情况,如果字段标签是“ - ”,则该字段总是被省略。一般用于数据库的ID字段。
json:
"-"
-
golang是静态类型语言,对于类型定义的是不能动态修改。在json处理当中,struct tag的string可以起到部分动态类型的效果。有时候输出的json希望是数字的字符串,而定义的字段是数字类型,那么就可以使用string选项。
type Account struct {
Email string `json:"-"`
Password string `json:"password,omitempty"`
Money float64 `json:"money,string"`
}
2、流式编码器方法 : 编码json文件
json.Marshal实际上只是对json.Encoder的封装,因此使用json.Encoder同样可以编码JSON。函数原型如下:
// Encode writes the JSON encoding of v to the stream,
// followed by a newline character.
//
func (enc *Encoder) Encode(v interface{}) error {}
调用示例:
func main(){
b := &bytes.Buffer{}
encoder := json.NewEncoder(b)
err := encoder.Encode(结构体/map类型值)
if err != nil{
panic(err)
}
fmt.Println(b.String())
}
json的反序列化
1、Unmarshal方法: 将json数据反序列化为其它数据结构
//将json反序列化成struct对象将json 反序列化到map中将json反序列化到slice中
func Unmarshal(data []byte, v interface{}) error {
// Check for well-formedness.
// Avoids filling out half a data structure
// before discovering a JSON syntax error.
var d decodeState
err := checkValid(data, &d.scan)
if err != nil {
return err
}
d.init(data)
return d.unmarshal(v)
}
2、流式解码器方法: 将json数据反序列化为其它数据结构
调用json的NewDecoder方法构造一个Decode对象,最后使用这个对象的Decode方法赋值给定义好的结构对象。对于字串,可是使用strings.NewReader
方法,让字串变成一个Stream对象。对于json数组而言,json.Decoder会一个一个json元素进行加载,不会把整个json数组读到内存里面。
函数原型:
// func NewDecoder(r io.Reader) *Decoder
// func (dec *Decoder) Decode(v interface{}) error
err = json.NewDecoder(resp.Body).Decode(value)
代码示例:
var jsonString string = `{
"username":"rsj217@gmail.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.Fatalln(err)
}
fmt.Printf("%#v\n",user)
}
json.Decoder和json.Unmarshal的场景区别:
-
Use json.Decoder if your data is coming from an io.Reader stream, or you need to decode multiple values from a stream of data. For the case of reading from an HTTP request, I’d pick json.Decoder since you’re obviously reading from a stream.
-
Use json.Unmarshal if you already have the JSON data in memory.
json RawMessage 延迟解析
RawMessag是原始编码后的json值。含json.RawMessage字段的结构,在反序列化时会完整的接收json串对象的[]byte形式。延迟解析在实际使用时才解析它的具体类型。使用json.RawMessage方式,将json的字串继续以byte数组方式存在。
// RawMessage is a raw encoded JSON value.
// It implements Marshaler and Unmarshaler and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawMessage []byte
// MarshalJSON returns m as the JSON encoding of m.
func (m RawMessage) MarshalJSON() ([]byte, error) {
if m == nil {
return []byte("null"), nil
}
return m, nil
}
// UnmarshalJSON sets *m to a copy of data.
func (m *RawMessage) UnmarshalJSON(data []byte) error {
if m == nil {
return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
}
*m = append((*m)[0:0], data...)
return nil
}
代码分析
// test ...
type test struct {
WorkLoadTemplate json.RawMessage `json:"workload_template"`
}
// test2 ...
type test2 struct {
WorkLoadTemplate string `json:"workload_template"`
}
testjson :=
`{
"workload_template":{"kind":"Deployment","apiVersion":"apps/v1","metadata":{"name":"tars.tarsnotify.test3","namespace":"center","creationTimestamp":null},"spec":{"replicas":1,"selector":null,"template":{"metadata":{"creationTimestamp":null},"spec":{"containers":[{"name":"application","image":"tars.dockerhub.com:5000/darren/tarsnotify:1.0.0","resources":{},"imagePullPolicy":"IfNotPresent"}]}},"strategy":{"type":"RollingUpdate","rollingUpdate":{"maxUnavailable":0,"maxSurge":1}}},"status":{}}
}`
-
testjson串 反序列化到test结构体会成功,json.RawMessage使得该字段保留原始的json串。
-
testjson串反序列化到test2结构体失败,因为在json串中workload_template对应的是一个json对象即go中的结构体,map等。
testjson2 := `{
"workload_template":"{\"kind\":\"Deployment\",\"apiVersion\":\"apps/v1\",\"metadata\":{\"name\":\"tars.tarsnotify.test3\",\"namespace\":\"center\",\"creationTimestamp\":null},\"spec\":{\"replicas\":1,\"selector\":null,\"template\":{\"metadata\":{\"creationTimestamp\":null},\"spec\":{\"containers\":[{\"name\":\"application\",\"image\":\"tars.dockerhub.com:5000/darren/tarsnotify:1.0.0\",\"resources\":{},\"imagePullPolicy\":\"IfNotPresent\"}]}},\"strategy\":{\"type\":\"RollingUpdate\",\"rollingUpdate\":{\"maxUnavailable\":0,\"maxSurge\":1}}},\"status\":{}}"
}`
-
testjson串反序列到test2结构体成功。因为在json串中workload_template对应的是一个string对象即go中的string类型。
var d test
var d2 test2
testjson="1"
t.WorkLoadTemplate=[]byte(testjson)
t2.WorkLoadTemplate=testjson
fmt.Printf("d: %v", d) // 字节的ascii码形式 {[49]}
fmt.Printf("d2: %v", d2) //结构体字符串 {1}
jsonstr, err = json.Marshal(d)
fmt.Printf("d: %v", string(jsonstr)) // {"workload_template":1}
jsonstr, err = json.Marshal(d2)
fmt.Printf("d2: %v", string(jsonstr)) // {"workload_template":"1"}
- 基于同一个string串“1”的序列化结果不一致,因为在两个结构体中的workload_template数据类型不一致,不同类型的编码器不同,导致编码结果不同。
不定字段解析
对于未知json结构的解析,不同的数据类型可以映射到接口或者使用延迟解析。有时候,会遇到json的数据字段都不一样的情况。
1、interface{}方法。例如需要解析下面一个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:"name"`
Age int `json:"age"`
}
type Place struct {
City string `json:"city"`
Country 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
}
2、RawMessage方法
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.RawMessage
err := 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
}