Go与Json

2,864次阅读

共计 4537 个字符,预计需要花费 12 分钟才能阅读完成。

Go像其它语言一样提供了很多标准库, JSON也是其中之一。官网也提供了完整的 文档,但是对于初学者来说不是很好理解。所以今天们将学习下 Go 语言 JSON 库的用法.

JSON 与静态类型

对于像 Go 这种静态类型语言来说 JSON 这种数据格式会有一些问题。JSON的内容可以是任何内容,编译器是如何知道内存放置的任何内容呢?

这里有两个答案,当你名确知道数据的字段、类型时,可以将 JSON 解析为你定义的结构体. 任何不适合结构体中的字段都被忽略。下面代码将 JSON 字符串转为结构体.

解析为结构体


package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Id int32 `json:"id"`
    Name string `json:"name"`
}

func main() {jsonString := []byte(`
        {"id": 1, "name": "meShell"}   
    `)
    var meShell User
    json.Unmarshal(jsonString, &meShell) // 将内容解析到 meShell 变量上

    fmt.Printf("%+v", meShell)
}

你会发现 GoUnmarshal作为转换函数运行之后的结果.


meShell: {Id:1 Name:meShell}
Process finished with exit code 0

结构体到 JSON 字符串

将结构体转为 [JSON][] 数据类型字符串与上面一样只是函数名改变.


stringJson, _ := json.Marshal(meShell)

与 Go 中的所有结构一样,重要的是要记住,只有具有大写第一个字母的字段对于 JSON Marshaller 等外部程序是可见的。


type jsonStruct struct {
    hidden int32
    Show int32
}
jsonStructString, _ := json.Marshal(jsonStruct{hidden: 1, Show: 2})
fmt.Printf("%s", jsonStructString)

大家可以运行下上面的代码的打印结果就知道了上面说得外部程序可见是什么意思.

结构标签

你会注意到我们的结构中反引号之间包含的”标记”数据。JSON 解析器从那几条关于如何解析该值的线索中读取。

在上面的示例中你已知道,Go 要求所有导出的字段都以大写字母开头。然而,在 JSON 中使用该样式并不常见。所以我们使用标记让解析器知道实际查找值的位置。

就像下面的代码一样:


type MyStruct struct {SomeField string `json:"some_field"`}

值为空怎么办

JSON 解析器还接受标记中的标记,以便在字段为空时让它知道该怎么做。 omitempty标志告诉它在输出中不包含 JSON 值,如果它是该类型的”空值”。


type MyStruct struct {
    //SomeField string `json:"some_field"`  // 大家可以打印下加和不加的结果区别
    SomeField string `json:"some_field,omitempty"`
}
stringJson, _ := json.Marshal(MyStruct{})
fmt.Printf("%s", stringJson)

如果 if some_field ==“”:

  • 加上omitempty  结果将是{}.
  • 不加omitempty  结果将是{“some_field”:“”}.

跳过字段

要让 JSON 解析器跳过一个字段,只需给它命名 json:"-" 即可.


type User struct {
    Id int32
    Name string
    Password string `json:"-"`
}
stringJson, _ := json.Marshal(User{})

fmt.Printf("%s", stringJson)

运行结果:{"Id":0,"Name":""}.

嵌套字段

嵌套字段是指作为其他结构的属性的结构。您可以在结构中嵌套切片或其他结构,JSON 将以递归方式正确解析所有内容。如果您有一个可以是任何内容的字段,则可以始终使用 interface {} 类型。


    type User struct {Id int32 `json:"id"`}

    type Role struct {
        Id int32 `json:"-"`
        Name string `json:"name"`
        Time string `json:"time"`
    }

    type UserWithRole struct {
        User 
        Role 
    }
    var userWithRole UserWithRole
    data := []byte(`{"Id": 1, "Name": "管理员"}`)
    json.Unmarshal(data, &userWithRole)

    fmt.Printf("%+v", userWithRole)

运行结果:{User:{Id:1} Role:{Id:0 Name: 管理员 Time:}}.

处理错误

务必检查 Marshal 和 Unmarshal 返回的 err 参数。例如,您将如何知道您正在解析的 JSON 的语法是否存在错误。如果不进行检查,程序将继续执行已经创建的清零结构,这可能会导致下面的程序逻辑混乱。

如果你不想处理每个转换的处理错误,并且转换错误是不常见的,你可以自己定义一个通用函数将错误转换为异常:


func CommonMarshal(data interface{}) []byte {out, err := json.Marshal(data)
    if err != nil {panic(err)
    }
    return out
}

func CommonUnmarshal([]byte jsonString, data interface{}) {err := json.Unmarshal(jsonString, &data)
    if err != nil {panic(err)
    }
}

解码任意数据

如果您真的不知道您的 JSON 可能是什么样的,您可以将其解析为通用 interface {}。如果您不熟悉空interface {} 是一种在 Go 中定义变量的方法,“这可能是任何东西”。 
在运行时,Go 将分配适当的内存以适合您决定存储在其中的任何内容。


var parsed interface{}
json.Unmarshal([]byte(`{"id": 1, "name": "meShell"}`), &parsed)

fmt.Printf("%+v", parsed)

json 包使用六种类型解析内容:

  • map[string]interface{}  任意数据对象
  • []interface{}  任意数组对象
  • bool
  • float64
  • string
  • nil

像上面的示例代码我们可以通过 parsed.(map[string]interface{}) 断言他是这种类型.


jsonObject := parsed.(map[string]interface{})

for k, v := range jsonObject {switch vv := v.(type) {
    case string:
        fmt.Println(k, "is string", 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")
    }
}

运行结果:


id is float64 1
name is string meShell

自定义对象 JSON

你可以通过文档发现 JSON 模块包括两个接口 [Marshaler][] 和[Unmarshaler][]下面将通过示例来展示.


package main

import (
    "fmt"
    "strconv"
    "strings"
)

type User struct {
    Id int32
    Name string
}

func (user User) MarshalJSON() ([]byte, error) {return []byte(fmt.Sprintf("%d|%s", user.Id, user.Name)), nil
}

func (user *User) UnmarshalJSON(value []byte) (error) {split := strings.Split(string(value), "|")

    id, _ := strconv.ParseInt(split[0], 10, 32)

    user.Id = int32(id)

    user.Name = split[1]

    return nil
}

func main() {user := User{Id: 9527, Name:"meShell"}

    jsonString, _ := user.MarshalJSON()
    fmt.Printf("%s\n", jsonString)

    userTwo := new(User)

    userTwo.UnmarshalJSON([]byte("100| 骑驴找蚂蚁"))

    fmt.Printf("%+v\n", userTwo)
}

运行结果:


9527|meShell
&{Id:100 Name: 骑驴找蚂蚁}

延迟处理

使用 RawMessage 来延迟解析部分 JSON 消息


package main

import (
    "encoding/json"
    "fmt"
    "log"
)

func main() {
    type Color struct {
        Space string
        Point json.RawMessage // delay parsing until we know the color space
    }
    type RGB struct {
        R uint8
        G uint8
        B uint8
    }
    type YCbCr struct {
        Y  uint8
        Cb int8
        Cr int8
    }

    var j = []byte(`[{"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}},
    {"Space": "RGB",   "Point": {"R": 98, "G": 218, "B": 255}}
]`)
    var colors []Color
    err := json.Unmarshal(j, &colors)
    if err != nil {log.Fatalln("error:", err)
    }

    for _, c := range colors {var dst interface{}
        switch c.Space {
        case "RGB":
            dst = new(RGB)
        case "YCbCr":
            dst = new(YCbCr)
        }
        err := json.Unmarshal(c.Point, dst)
        if err != nil {log.Fatalln("error:", err)
        }
        fmt.Println(c.Space, dst)
    }
}

Go 的 JSON 讲解决我们就到此为止了,各位可以跑下文章中的代码示例.

推荐阅读

  1. https://blog.golang.org/json-and-go
  2. https://golang.org/pkg/encoding/json

正文完
 
Blood.Cold
版权声明:本站原创文章,由 Blood.Cold 2019-06-08发表,共计4537字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。