Go語言 Web開發(fā)(8)JSON處理


JavaScript Object Notation(JSON)是一個(gè)數(shù)據(jù)交換標(biāo)準(zhǔn),因其簡單、可讀性強(qiáng)廣泛使用。Go語言中有一個(gè)"encoding/json"標(biāo)準(zhǔn)包,里面有完整的解析JSON字符串的功能支持

Go類型和JSON類型的對(duì)應(yīng)關(guān)系如下:

  • bool 代表 JSON booleans,
  • 浮點(diǎn)數(shù), 整數(shù), Number -> JSON number,
  • string 代表 JSON strings,
  • nil 代表 JSON null.
  • 數(shù)組、切片 -> JSON數(shù)組
  • struct、map -> JSON object

(1)將JSON數(shù)據(jù)解析成結(jié)構(gòu)體,需要用到 Unmarshal 函數(shù),該函數(shù)的源碼如下

func Unmarshal(data []byte, v interface{}) error {
    //檢查結(jié)構(gòu)良好。
    //避免填寫半數(shù)據(jù)結(jié)構(gòu)
    //在發(fā)現(xiàn)JSON語法錯(cuò)誤之前
   var d decodeState
   err := checkValid(data, &d.scan)
   if err != nil {
      return err
   }
   d.init(data)
   return d.unmarshal(v)
}

解析示例
首先定義一個(gè)結(jié)構(gòu)體與數(shù)組切片

//與json數(shù)據(jù)對(duì)應(yīng)的結(jié)構(gòu)體
type Server struct {
   ServerName string
   ServerIP   string
}
// 數(shù)組對(duì)應(yīng)slice
type ServerSlice struct {
   Servers []Server
}

然后將JSON數(shù)據(jù)解析成結(jié)構(gòu)體

package main

import (
   "encoding/json"
   "fmt"
)
func main()  {
   var s ServerSlice
   str := `{"servers":[{"serverName":"TianJin","serverIP":"127.0.0.1"},
{"serverName":"Beijing","serverIP":"127.0.0.2"}]}`
   json.Unmarshal([]byte(str), &s)
   fmt.Println(s)
}

----output-----
{[{TianJin 127.0.0.1} {Beijing 127.0.0.2}]}

按照上述代碼可以正常的將JSON解析成結(jié)構(gòu)體,JSON在struct字段相中查找對(duì)應(yīng)的字段有以下幾個(gè)規(guī)則:
①首先會(huì)查找struct字段中所有的可導(dǎo)出的struct字段(所謂可導(dǎo)出就是首字母大寫),然后去自己的JSON數(shù)據(jù)中找對(duì)應(yīng)字段的數(shù)據(jù)
②JSON數(shù)據(jù)中對(duì)應(yīng)的字段無關(guān)大小寫,只要值相同即可

因此上面的Server 結(jié)構(gòu)體中的 ServerName 改為 serverName的話,輸出結(jié)果就只有后面的 ServerIP 了

{[{ 127.0.0.1} { 127.0.0.2}]}

(2)將結(jié)構(gòu)體解析為JSON數(shù)據(jù),需要用到 Unmarshal 函數(shù),該函數(shù)的源碼如下

func Marshal(v interface{}) ([]byte, error) {
   e := &encodeState{}
   err := e.marshal(v, encOpts{escapeHTML: true})
   if err != nil {
      return nil, err
   }
   return e.Bytes(), nil
}

將結(jié)構(gòu)體解析成JSON數(shù)據(jù)

func main()  {
   var s ServerSlice
   s.Servers = append(s.Servers,Server{ServerName:"TianJin",
ServerIP:"127.0.0.1"})
   s.Servers = append(s.Servers,Server{ServerName:"BeiJing",
ServerIP:"127.0.0.1"})
   if data, err := json.Marshal(s); err == nil {
      fmt.Printf("%s\n", data)
   }
}
---output---
{"Servers":[{"ServerName":"TianJin","ServerIP":"127.0.0.1"},
{"ServerName":"BeiJing","ServerIP":"127.0.0.1"}]}

如果我們要自己定義生成的JSON數(shù)據(jù)的字段名呢?我們可以通過 struct tag定義來實(shí)現(xiàn)

//與json數(shù)據(jù)對(duì)應(yīng)的結(jié)構(gòu)體
type Server struct {
   ServerName string `json:"serverName1"`
   ServerIP string `json:"serverIP2"`
}
// 數(shù)組對(duì)應(yīng)slice
type ServerSlice struct {
   Servers []Server `json:"servers3"`
}

上面的JSON就會(huì)變?yōu)橄旅娼Y(jié)果

{"servers3":[{"serverName1":"TianJin","serverIP2":"127.0.0.1"},
{"serverName1":"BeiJing","serverIP2":"127.0.0.1"}]}

針對(duì)JSON的輸出,我們?cè)诙xstruct tag的時(shí)候需要注意的幾點(diǎn)是:
1 字段的tag是 "-" ,那么這個(gè)字段不會(huì)輸出到JSON
2 tag中帶有自定義名稱,那么這個(gè)自定義名稱會(huì)出現(xiàn)在JSON的字段名中,例如上面例子中serverName
3 tag中如果帶有 "omitempty" 選項(xiàng),那么如果該字段值為空,就不會(huì)輸出到JSON串中
4 如果字段類型是bool, string, int, int64等,而tag中帶有 ",string" 選項(xiàng),那么這個(gè)字段在輸出到JSON的時(shí)候會(huì)把該字段對(duì)應(yīng)的值轉(zhuǎn)換成JSON字符串
舉例說明

type Server struct {
   // ID  不會(huì)導(dǎo)出到 JSON 中
   ID int `json:"-"`
   // ServerName 會(huì)在JSON中被 serverName 覆蓋
   ServerName string `json:"serverName"`
    //轉(zhuǎn)換成JSON字符串
   ServerName2 string `json:"serverName2,string"`
   //  如果 ServerIP  為空,則不輸出到 JSON 串中
   ServerIP string `json:"serverIP,omitempty"`
}
s := Server {
   ID: 3,
   ServerName: `Go "1.0" `,
   ServerName2: `Go "1.0" `,
   ServerIP: ``,
}
b, _ := json.Marshal(s)
os.Stdout.Write(b)
會(huì)輸出以下內(nèi)容:
{"serverName":"Go \"1.0\" ","serverName2":"\"Go \\\"1.0\\\" \""}

Marshal函數(shù)只有在轉(zhuǎn)換成功的時(shí)候才會(huì)返回?cái)?shù)據(jù),在轉(zhuǎn)換的過程中我們需要注意幾點(diǎn):
①JSON對(duì)象只支持string作為key,所以要編碼一個(gè)map,那么必須是map[string]T這種類型(T是Go語言中任意的類型)
②Channel, complex和function是不能被編碼成JSON的
③嵌套的數(shù)據(jù)是不能編碼的,不然會(huì)讓JSON編碼進(jìn)入死循環(huán)
④指針在編碼的時(shí)候會(huì)輸出指針指向的內(nèi)容,而空指針會(huì)輸出null

上面解析方式是在我們知曉被解析的JSON數(shù)據(jù)的結(jié)構(gòu)的前提下采取的方案,如果我們不知道被解析的數(shù)據(jù)的格式,又應(yīng)該如何來解析呢?
(3)將JSON解析到interface

現(xiàn)在我們假設(shè)有如下的JSON數(shù)據(jù)
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
在我們不知道他的結(jié)構(gòu)的情況下,我們把他解析到interface{}里面
var f interface{}
err := json.Unmarshal(jsonData, &f)

這個(gè)時(shí)候f里面存儲(chǔ)了一個(gè)map類似,他們的key是string,值存儲(chǔ)在空的interface{}里

f = map[string]interface{}{
    "Name": "Wednesday",
    "Age": 6,
    "Parents": []interface{}{
                "Gomez",
                "Morticia",
    },
}

那么如何來訪問這些數(shù)據(jù)呢?通過斷言的方式:

m := f.(map[string]interface{})
通過斷言之后,你就可以通過如下方式來訪問里面的數(shù)據(jù)了
for k, v := range m {
   switch vv := v.(type) {
   case string:
      fmt.Println(k, "is string", vv)
   case int:
      fmt.Println(k, "is int", vv)
   case []interface{}:
      fmt.Println(k, "is an array:")
      for i, u := range vv {
         fmt.Println(i, u)
      }
   default:
      fmt.Println(k, "is of a type I don't know how to handle")
   }
}

參考書籍:《Go Web編程》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • JSON http的交互的生命周期包含請(qǐng)求和響應(yīng)。前面我們介紹了很多關(guān)于發(fā)起請(qǐng)求,處理請(qǐng)求的內(nèi)容。現(xiàn)在該聊一聊返回...
    人世間閱讀 37,543評(píng)論 4 41
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 32,275評(píng)論 2 89
  • 浮沉苦樂但自知,舉杯痛飲話如癡。酒過三巡清風(fēng)笑,夢(mèng)里尋她人逍遙。 <人生的浮浮沉沉、喜怒哀樂一般都只有自己知道,一...
    三古月南閱讀 264評(píng)論 5 2
  • 地球是近圓的,太陽是圓的,八卦圖也是圓的。我們的生活也不過在重復(fù)的畫圓。 春夏秋冬,四季輪回。日升月落,晝夜交替,...
    逗霸君閱讀 720評(píng)論 0 9

友情鏈接更多精彩內(nèi)容