在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ GO/ 7.2 JSON處理
7 文本處理
3 Web基礎(chǔ)
14 擴(kuò)展Web框架
10.4 小結(jié)
2.2 Go基礎(chǔ)
2.8 總結(jié)
6.1 session和cookie
5.5 使用beedb庫(kù)進(jìn)行ORM開(kāi)發(fā)
8.3 REST
13.6 小結(jié)
5.4 使用PostgreSQL數(shù)據(jù)庫(kù)
14.6 pprof支持
14.1 靜態(tài)文件支持
11.2 使用GDB調(diào)試
7.7 小結(jié)
1 GO環(huán)境配置
14.5 多語(yǔ)言支持
7.1 XML處理
1.5 總結(jié)
13 如何設(shè)計(jì)一個(gè)Web框架
14.3 表單及驗(yàn)證支持
12 部署與維護(hù)
10 國(guó)際化和本地化
1.1 Go 安裝
6.2 Go如何使用session
5.6 NOSQL數(shù)據(jù)庫(kù)操作
6.5 小結(jié)
9.4 避免SQL注入
12.1 應(yīng)用日志
4.2 驗(yàn)證表單的輸入
10.1 設(shè)置默認(rèn)地區(qū)
1.3 Go 命令
9.6 加密和解密數(shù)據(jù)
4.1 處理表單的輸入
4.4 防止多次遞交表單
11.3 Go怎么寫(xiě)測(cè)試用例
8 Web服務(wù)
12.3 應(yīng)用部署
5.7 小結(jié)
12.5 小結(jié)
11 錯(cuò)誤處理,調(diào)試和測(cè)試
9.2 確保輸入過(guò)濾
14.2 Session支持
6.4 預(yù)防session劫持
12.4 備份和恢復(fù)
8.1 Socket編程
13.1 項(xiàng)目規(guī)劃
13.4 日志和配置設(shè)計(jì)
7.6 字符串處理
13.2 自定義路由器設(shè)計(jì)
6.3 session存儲(chǔ)
3.4 Go的http包詳解
8.2 WebSocket
10.3 國(guó)際化站點(diǎn)
7.5 文件操作
7.4 模板處理
9.1 預(yù)防CSRF攻擊
13.3 controller設(shè)計(jì)
2.6 interface
14.4 用戶(hù)認(rèn)證
2.3 流程和函數(shù)
附錄A 參考資料
11.1 錯(cuò)誤處理
9.5 存儲(chǔ)密碼
9.3 避免XSS攻擊
12.2 網(wǎng)站錯(cuò)誤處理
6 session和數(shù)據(jù)存儲(chǔ)
2.4 struct類(lèi)型
3.3 Go如何使得Web工作
2.5 面向?qū)ο?/span>
3.1 Web工作方式
1.2 GOPATH與工作空間
2.1 你好,Go
9.7 小結(jié)
13.5 實(shí)現(xiàn)博客的增刪改
7.2 JSON處理
10.2 本地化資源
7.3 正則處理
2 Go語(yǔ)言基礎(chǔ)
5.1 database/sql接口
4.5 處理文件上傳
8.5 小結(jié)
4.3 預(yù)防跨站腳本
5.3 使用SQLite數(shù)據(jù)庫(kù)
14.7 小結(jié)
3.2 Go搭建一個(gè)Web服務(wù)器
2.7 并發(fā)
5 訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)
4 表單
3.5 小結(jié)
1.4 Go開(kāi)發(fā)工具
11.4 小結(jié)
9 安全與加密
5.2 使用MySQL數(shù)據(jù)庫(kù)
4.6 小結(jié)
8.4 RPC

7.2 JSON處理

JSON(Javascript Object Notation)是一種輕量級(jí)的數(shù)據(jù)交換語(yǔ)言,以文字為基礎(chǔ),具有自我描述性且易于讓人閱讀。盡管JSON是Javascript的一個(gè)子集,但JSON是獨(dú)立于語(yǔ)言的文本格式,并且采用了類(lèi)似于C語(yǔ)言家族的一些習(xí)慣。JSON與XML最大的不同在于XML是一個(gè)完整的標(biāo)記語(yǔ)言,而JSON不是。JSON由于比XML更小、更快,更易解析,以及瀏覽器的內(nèi)建快速解析支持,使得其更適用于網(wǎng)絡(luò)數(shù)據(jù)傳輸領(lǐng)域。目前我們看到很多的開(kāi)放平臺(tái),基本上都是采用了JSON作為他們的數(shù)據(jù)交互的接口。既然JSON在Web開(kāi)發(fā)中如此重要,那么Go語(yǔ)言對(duì)JSON支持的怎么樣呢?Go語(yǔ)言的標(biāo)準(zhǔn)庫(kù)已經(jīng)非常好的支持了JSON,可以很容易的對(duì)JSON數(shù)據(jù)進(jìn)行編、解碼的工作。

前一小節(jié)的運(yùn)維的例子用json來(lái)表示,結(jié)果描述如下:

{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}

本小節(jié)余下的內(nèi)容將以此JSON數(shù)據(jù)為基礎(chǔ),來(lái)介紹go語(yǔ)言的json包對(duì)JSON數(shù)據(jù)的編、解碼。

解析JSON

解析到結(jié)構(gòu)體

假如有了上面的JSON串,那么我們?nèi)绾蝸?lái)解析這個(gè)JSON串呢?Go的JSON包中有如下函數(shù)

func Unmarshal(data []byte, v interface{}) error

通過(guò)這個(gè)函數(shù)我們就可以實(shí)現(xiàn)解析的目的,詳細(xì)的解析例子請(qǐng)看如下代碼:

package main

import (
    "encoding/json"
    "fmt"
)

type Server struct {
    ServerName string
    ServerIP   string
}

type Serverslice struct {
    Servers []Server
}

func main() {
    var s Serverslice
    str := `{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}`
    json.Unmarshal([]byte(str), &s)
    fmt.Println(s)
}

在上面的示例代碼中,我們首先定義了與json數(shù)據(jù)對(duì)應(yīng)的結(jié)構(gòu)體,數(shù)組對(duì)應(yīng)slice,字段名對(duì)應(yīng)JSON里面的KEY,在解析的時(shí)候,如何將json數(shù)據(jù)與struct字段相匹配呢?例如JSON的key是Foo,那么怎么找對(duì)應(yīng)的字段呢?

  • 首先查找tag含有Foo的可導(dǎo)出的struct字段(首字母大寫(xiě))
  • 其次查找字段名是Foo的導(dǎo)出字段
  • 最后查找類(lèi)似FOO或者FoO這樣的除了首字母之外其他大小寫(xiě)不敏感的導(dǎo)出字段

聰明的你一定注意到了這一點(diǎn):能夠被賦值的字段必須是可導(dǎo)出字段(即首字母大寫(xiě))。同時(shí)JSON解析的時(shí)候只會(huì)解析能找得到的字段,找不到的字段會(huì)被忽略,這樣的一個(gè)好處是:當(dāng)你接收到一個(gè)很大的JSON數(shù)據(jù)結(jié)構(gòu)而你卻只想獲取其中的部分?jǐn)?shù)據(jù)的時(shí)候,你只需將你想要的數(shù)據(jù)對(duì)應(yīng)的字段名大寫(xiě),即可輕松解決這個(gè)問(wèn)題。

解析到interface

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

我們知道interface{}可以用來(lái)存儲(chǔ)任意數(shù)據(jù)類(lèi)型的對(duì)象,這種數(shù)據(jù)結(jié)構(gòu)正好用于存儲(chǔ)解析的未知結(jié)構(gòu)的json數(shù)據(jù)的結(jié)果。JSON包中采用map[string]interface{}和[]interface{}結(jié)構(gòu)來(lái)存儲(chǔ)任意的JSON對(duì)象和數(shù)組。Go類(lèi)型和JSON類(lèi)型的對(duì)應(yīng)關(guān)系如下:

  • bool 代表 JSON booleans,
  • float64 代表 JSON numbers,
  • string 代表 JSON strings,
  • nil 代表 JSON null.

現(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(b, &f)

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

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

那么如何來(lái)訪(fǎng)問(wèn)這些數(shù)據(jù)呢?通過(guò)斷言的方式:

m := f.(map[string]interface{})

通過(guò)斷言之后,你就可以通過(guò)如下方式來(lái)訪(fǎng)問(wèn)里面的數(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 float64:
        fmt.Println(k,"is float64",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")
    }
}

通過(guò)上面的示例可以看到,通過(guò)interface{}與type assert的配合,我們就可以解析未知結(jié)構(gòu)的JSON數(shù)了。

上面這個(gè)是官方提供的解決方案,其實(shí)很多時(shí)候我們通過(guò)類(lèi)型斷言,操作起來(lái)不是很方便,目前bitly公司開(kāi)源了一個(gè)叫做simplejson的包,在處理未知結(jié)構(gòu)體的JSON時(shí)相當(dāng)方便,詳細(xì)例子如下所示:

js, err := NewJson([]byte(`{
    "test": {
        "array": [1, "2", 3],
        "int": 10,
        "float": 5.150,
        "bignum": 9223372036854775807,
        "string": "simplejson",
        "bool": true
    }
}`))

arr, _ := js.Get("test").Get("array").Array()
i, _ := js.Get("test").Get("int").Int()
ms := js.Get("test").Get("string").MustString()

可以看到,使用這個(gè)庫(kù)操作JSON比起官方包來(lái)說(shuō),簡(jiǎn)單的多,詳細(xì)的請(qǐng)參考如下地址:https://github.com/bitly/go-simplejson

生成JSON

我們開(kāi)發(fā)很多應(yīng)用的時(shí)候,最后都是要輸出JSON數(shù)據(jù)串,那么如何來(lái)處理呢?JSON包里面通過(guò)Marshal函數(shù)來(lái)處理,函數(shù)定義如下:

func Marshal(v interface{}) ([]byte, error)

假設(shè)我們還是需要生成上面的服務(wù)器列表信息,那么如何來(lái)處理呢?請(qǐng)看下面的例子:

package main

import (
    "encoding/json"
    "fmt"
)

type Server struct {
    ServerName string
    ServerIP   string
}

type Serverslice struct {
    Servers []Server
}

func main() {
    var s Serverslice
    s.Servers = append(s.Servers, Server{ServerName: "Shanghai_VPN", ServerIP: "127.0.0.1"})
    s.Servers = append(s.Servers, Server{ServerName: "Beijing_VPN", ServerIP: "127.0.0.2"})
    b, err := json.Marshal(s)
    if err != nil {
        fmt.Println("json err:", err)
    }
    fmt.Println(string(b))
}

輸出如下內(nèi)容:

{"Servers":[{"ServerName":"Shanghai_VPN","ServerIP":"127.0.0.1"},{"ServerName":"Beijing_VPN","ServerIP":"127.0.0.2"}]}

我們看到上面的輸出字段名的首字母都是大寫(xiě)的,如果你想用小寫(xiě)的首字母怎么辦呢?把結(jié)構(gòu)體的字段名改成首字母小寫(xiě)的?JSON輸出的時(shí)候必須注意,只有導(dǎo)出的字段才會(huì)被輸出,如果修改字段名,那么就會(huì)發(fā)現(xiàn)什么都不會(huì)輸出,所以必須通過(guò)struct tag定義來(lái)實(shí)現(xiàn):

type Server struct {
    ServerName string `json:"serverName"`
    ServerIP   string `json:"serverIP"`
}

type Serverslice struct {
    Servers []Server `json:"servers"`
}

通過(guò)修改上面的結(jié)構(gòu)體定義,輸出的JSON串就和我們最開(kāi)始定義的JSON串保持一致了。

針對(duì)JSON的輸出,我們?cè)诙xstruct tag的時(shí)候需要注意的幾點(diǎn)是:

  • 字段的tag是"-",那么這個(gè)字段不會(huì)輸出到JSON
  • tag中帶有自定義名稱(chēng),那么這個(gè)自定義名稱(chēng)會(huì)出現(xiàn)在JSON的字段名中,例如上面例子中serverName
  • tag中如果帶有"omitempty"選項(xiàng),那么如果該字段值為空,就不會(huì)輸出到JSON串中
  • 如果字段類(lèi)型是bool, string, int, int64等,而tag中帶有",string"選項(xiàng),那么這個(gè)字段在輸出到JSON的時(shí)候會(huì)把該字段對(duì)應(yīng)的值轉(zhuǎn)換成JSON字符串

舉例來(lái)說(shuō):

type Server struct {
    // ID 不會(huì)導(dǎo)出到JSON中
    ID int `json:"-"`

    // ServerName 的值會(huì)進(jìn)行二次JSON編碼
    ServerName  string `json:"serverName"`
    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)換的過(guò)程中我們需要注意幾點(diǎn):

  • JSON對(duì)象只支持string作為key,所以要編碼一個(gè)map,那么必須是map[string]T這種類(lèi)型(T是Go語(yǔ)言中任意的類(lèi)型)
  • Channel, complex和function是不能被編碼成JSON的
  • 嵌套的數(shù)據(jù)是不能編碼的,不然會(huì)讓JSON編碼進(jìn)入死循環(huán)
  • 指針在編碼的時(shí)候會(huì)輸出指針指向的內(nèi)容,而空指針會(huì)輸出null

本小節(jié),我們介紹了如何使用Go語(yǔ)言的json標(biāo)準(zhǔn)包來(lái)編解碼JSON數(shù)據(jù),同時(shí)也簡(jiǎn)要介紹了如何使用第三方包go-simplejson來(lái)在一些情況下簡(jiǎn)化操作,學(xué)會(huì)并熟練運(yùn)用它們將對(duì)我們接下來(lái)的Web開(kāi)發(fā)相當(dāng)重要。