Go操作JSON

简介

JavaScript Object Notation(JSON)是一个数据交换标准,因其简单、可读性强广泛使用。Go的标准包encoding/json对JSON的编解码提供了完整的支持。

编码

编码即将Go数据类型转换为JSON。用到的函数:

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

该函数递归遍历v的结构,生成对应的JSON例如:

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    m := map[string][]string{
        "level":   {"debug"},
        "message": {"File not found", "Stack overflow"},
    }

    if data, err := json.Marshal(m); err == nil {
        fmt.Printf("%s\n", data)
    }
}

输出:

{"level":["debug"],"message":["File not found","Stack overflow"]}

在编码过程中,json包会将Go的类型转换为JSON类型,转换规则如下:
bool -> JSON boolean
浮点数, 整数, Number -> JSON number
string -> JSON string
数组、切片 -> JSON数组
[]byte -> base64 string
struct、map -> JSON object

结构体转JSON

经常会使用结构体来转换成JSON。json包是通过反射机制来实现编解码的,因此结构体必须导出所转换的字段,不导出的字段不会被json包解析:

package main

import (
    "encoding/json"
    "fmt"
)

type DebugInfo struct {
    Level  string
    Msg    string
    author string // 未导出字段不会被json解析
}

func main() {

    dbgInfs := []DebugInfo{
        DebugInfo{"debug", `File: "test.txt" Not Found`, "Cynhard"},
        DebugInfo{"", "Logic error", "Gopher"},
    }

    if data, err := json.Marshal(dbgInfs); err == nil {
        fmt.Printf("%s\n", data)
    }
}

输出结果如下:

[{"Level":"debug","Msg":"File: \"test.txt\" Not Found"},{"Level":"","Msg":"Logic error"}]

结构体字段标签

json包在解析结构体时,如果遇到key为json的字段标签,则会按照一定规则解析该标签:第一个出现的是字段在JSON串中使用的名字,之后为其他选项,例如omitempty指定空值字段不出现在JSON中。如果整个value为"-",则不解析该字段。例如将上例中的结构体改为如下:

type DebugInfo struct {
    Level  string `json:"level,omitempty"` // Level解析为level,忽略空值
    Msg    string `json:"message"`         // Msg解析为message
    Author string `json:"-"`               // 忽略Author
}

则输出为:

[{"level":"debug","message":"File: \"test.txt\" Not Found"},{"message":"Logic error"}]

匿名字段

json包在解析匿名字段时,会将匿名字段的字段当成该结构体的字段处理:

package main

import (
    "encoding/json"
    "fmt"
)

type Point struct{ X, Y int }

type Circle struct {
    Point
    Radius int
}

func main() {
    if data, err := json.Marshal(Circle{Point{50, 50}, 25}); err == nil {
        fmt.Printf("%s\n", data)
    }
}

输出结果:

{"X":50,"Y":50,"Radius":25}

转换接口

在调用Marshal(v interface{})时,该函数会判断v是否满足json.Marshaler或者 encoding.Marshaler 接口,如果满足,则会调用这两个接口来进行转换(如果两个都满足,优先调用json.Marshaler)。这两个接口定义如下:

// json.Marshaler 
type Marshaler interface {
    MarshalJSON() ([]byte, error)
}

// encoding.TextMarshaler
type TextMarshaler interface {
    MarshalText() (text []byte, err error)
}

下例声明了MarshalJSON()函数,以使Point满足json.Marshaler接口:

package main

import (
    "encoding/json"
    "fmt"
)

type Point struct{ X, Y int }

func (pt Point)MarshalJSON() ([]byte, error) {
    return []byte(fmt.Sprintf(`{"X":%d,"Y":%d}`, pt.X, pt.Y)), nil
}

func main() {
    if data, err := json.Marshal(Point{50, 50}); err == nil {
        fmt.Printf("%s\n", data)
    }
}

结果如下:

{"X":50,"Y":50}

json包调用encoding.TextMarshaler接口处理转换时与调用json.Marshaler接口略有不同,例如将上例的MarshalJSON 改为MarshalText,使Point满足text.TextMarshaler

func (pt Point)MarshalText() ([]byte, error) {
    return []byte(fmt.Sprintf("{\"X\":%d,\"Y\":%d}", pt.X, pt.Y)), nil
}

则输出结果如下,可见json包在调用MarshalText时,给字符串加上了双引号:

"{\"X\":50,\"Y\":50}"

解码

将JSON转换为Go数据类型。用到的函数:

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

此函数将data表示的JSON转换为v:

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    data := `[{"Level":"debug","Msg":"File: \"test.txt\" Not Found"},` +
        `{"Level":"","Msg":"Logic error"}]`

    var dbgInfos []map[string]string
    json.Unmarshal([]byte(data), &dbgInfos)

    fmt.Println(dbgInfos)
}

输出为:

[map[Level:debug Msg:File: "test.txt" Not Found] map[Level: Msg:Logic error]]

在解码过程中,json包会将JSON类型转换为Go类型,转换规则如下:
JSON boolean -> bool
JSON number -> float64
JSON string -> string
JSON数组 -> []interface{}
JSON object -> map
null -> nil

JSON转结构体

JSON可以转换成结构体。同编码一样,json包是通过反射机制来实现解码的,因此结构体必须导出所转换的字段,不导出的字段不会被json包解析,另外解析时不区分大小写:

package main

import (
    "encoding/json"
    "fmt"
)

type DebugInfo struct {
    Level string
    Msg string
    author string  // 未导出字段不会被json解析
}

func (dbgInfo DebugInfo) String() string {
    return fmt.Sprintf("{Level: %s, Msg: %s}", dbgInfo.Level, dbgInfo.Msg)
}

func main() {
    data := `[{"level":"debug","msg":"File Not Found","author":"Cynhard"},` +
        `{"level":"","msg":"Logic error","author":"Gopher"}]`

    var dbgInfos []DebugInfo
    json.Unmarshal([]byte(data), &dbgInfos)

    fmt.Println(dbgInfos)
}

输出结果如下:

[{Level: debug, Msg: File Not Found} {Level: , Msg: Logic error}]

结构体字段标签

解码时依然支持结构体字段标签,规则和编码时一样:

package main

import (
    "encoding/json"
    "fmt"
)

type DebugInfo struct {
    Level  string `json:"level"`   // level 解码为 Level
    Msg    string `json:"message"` // message 解码为 Msg
    Author string `json:"-"`       // 忽略Author
}

func (dbgInfo DebugInfo) String() string {
    return fmt.Sprintf("{Level: %s, Msg: %s}", dbgInfo.Level, dbgInfo.Msg)
}

func main() {
    data := `[{"level":"debug","message":"File Not Found","author":"Cynhard"},` +
        `{"level":"","message":"Logic error","author":"Gopher"}]`

    var dbgInfos []DebugInfo
    json.Unmarshal([]byte(data), &dbgInfos)

    fmt.Println(dbgInfos)
}

则结果为:

[{Level: debug, Msg: File Not Found} {Level: , Msg: Logic error}]

匿名字段

编码时,和解码类似,在解码JSON时,如果找不到字段,则查找字段的字段:

package main

import (
    "encoding/json"
    "fmt"
)

type Point struct{ X, Y int }

type Circle struct {
    Point
    Radius int
}

func main() {

    data := `{"X":80,"Y":80,"Radius":40}`

    var c Circle
    json.Unmarshal([]byte(data), &c)

    fmt.Println(c)
}

输出结果:

{{80 80} 40}

转换接口

和编码类似,解码时根据参数是否满足json.Unmarshalerencoding.TextUnmarshaler来调用相应函数(若两个函数都存在,则优先调用UnmarshalJSON)。这两个接口定义如下:

// json.Unmarshaler
type Unmarshaler interface {
    UnmarshalJSON([]byte) error
}

// encoding.TextUnmarshaler
type TextUnmarshaler interface {
    UnmarshalText(text []byte) error
}

下例是一个使用json.Unmarshaler接口的例子(这里不实现解码算法,仅把参数打印出来):

package main

import (
    "encoding/json"
    "fmt"
)

type Point struct{ X, Y int }

func (Point) UnmarshalJSON(data []byte) error {
    fmt.Println(string(data))
    return nil
}

func main() {
    data := `{"X":80,"Y":80}`
    var pt Point
    json.Unmarshal([]byte(data), &pt)
}

输出如下:

{"X":80,"Y":80}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go 语言中,可以使用 `encoding/json` 包提供的 `Unmarshal` 函数将 JSON 字符串反序列化到 Go 结构体中。具体来说,可以按照以下步骤进行反序列化操作: 1. 根据 JSON 字符串的格式定义一个 Go 结构体类型。 例如,如果 JSON 字符串的格式为 `{"name": "Tom", "age": 18}`,则可以定义如下的 Go 结构体类型: ```go type Person struct { Name string `json:"name"` Age int `json:"age"` } ``` 其中,`json:"name"` 和 `json:"age"` 是结构体字段的标签,用于指定该字段在 JSON 中对应的键值。 2. 将 JSON 字符串转换成字节数组(`[]byte`)。 例如,如果 JSON 字符串为 `{"name": "Tom", "age": 18}`,则可以通过 `[]byte(jsonStr)` 将其转换成字节数组。 3. 调用 `json.Unmarshal` 函数将 JSON 字符串反序列化到 Go 结构体中。 例如,可以按照以下方式调用 `json.Unmarshal` 函数进行反序列化操作: ```go var p Person err := json.Unmarshal([]byte(jsonStr), &p) if err != nil { // 处理反序列化失败的错误 } ``` 在调用 `json.Unmarshal` 函数时,第一个参数为要反序列化的 JSON 字符串所对应的字节数组,第二个参数为一个指向要反序列化到的 Go 结构体对象的指针。在函数执行成功后,Go 结构体对象 `p` 就会被赋值为 JSON 字符串所对应的值。 需要注意的是,如果 JSON 字符串中的键值对与 Go 结构体中的字段不匹配,或者 JSON 字符串格式不正确,都有可能导致反序列化失败,此时需要进行错误处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值