乐趣区

Json and Go

Reference https://blog.go-zh.org/json-a…
Encoding
Encode 的基本用法是
package main

import (
“encoding/json”
“fmt”
“os”
)

type Message struct {
Name string
Body string
Time int64
}

func main() {
message := Message{“Tom”, “Hello”, 1294706395881547000}
b, err := json.Marshal(message)
if err != nil {
fmt.Fprintf(os.Stderr, “Failed to Marshal!”)
os.Exit(1)
}

fmt.Printf(“%s”, b)
}
输出为:
{“Name”:”Tom”,”Body”:”Hello”,”Time”:1294706395881547000}
func Marshal(v interface{}) ([]byte, error)
Only data structures that can be represented as valid JSON will be encoded:

JSON objects only support string as keys.

Channel, complex, and function types cannot be encoded.
Cyclic data structures are not supported.
Pointers will be encoded as the values they point to(or null if the pointer is nil)

json package 只能 access the exportede fields. 也就是首字母大写的 field. 也就是在 data structure 中的首字母大写的 field 才会 present in JSON output
Decoding
// We must first create a place where the decoded data will be stored
var output Message
// Please note that passing the pointer to output
decodeErr := json.Unmarshal(b, &output)
if decodeErr != nil {
fmt.Fprintf(os.Stderr, “Failed to Unmarshal json data!err:%s”, err)
os.Exit(1)
}

fmt.Printf(“%+v\n”, output)
Unmarshal 是怎么确认 json field 与 data structure 的对应关系呢?,其实是通过以下来判断的 (优先级从高到低). 比如对于 JSON Field “Foo” 来说,

An exported field with a tag of “Foo”.
An exported field named “Foo”
An exported field named “FOO” or “FoO” or some other case-insensitive match of “Foo”

总结下来是: Tag -> Foo -> FOO(case-insensitive match)tag 的判定规则如下
// Field appears in JSON as key “myName”.
Field int `json:”myName”`

// Field appears in JSON as key “myName” and
// the field is omitted from the object if its value is empty,
// as defined above.
Field int `json:”myName,omitempty”`

// Field appears in JSON as key “Field” (the default), but
// the field is skipped if empty.
// Note the leading comma.
Field int `json:”,omitempty”`

// Field is ignored by this package.
Field int `json:”-“`

// Field appears in JSON as key “-“.
Field int `json:”-,”`
如果 json data 与 data structure 中只有部分 field 匹配怎么办?
var unmatchedOutput UnmatchMessage
message1 :=
//` ` 代表原生字符串面值,没有转义操作,全是字符串的字面值
[]byte{`{“Name”:”Tom”,”Body”:”Hello”,”Time”:1294706395881547000}`}
decodeErr1 := json.Unmarshal(b, &unmatchedOutput)
if decodeErr1 != nil {
fmt.Fprintf(os.Stderr, “Failed to unmarshal json data! err:”, err)
os.Exit(1)
}
fmt.Printf(“%+v\n”, unmatchedOutput)
输出为
{Name:Tom Boy: Tim:0}
从上看出,Unmarshal 只会 decode 符合上述 3 条件的 field!This behavior is particularly useful when you wish to pick only a few specific fields out of a large JSON blob.
Generic JSON with interface{}
Decoding arbitrary data
以上 2 章先跳过去
Reference Types
Unmarshal 会为 Reference Types 自动 allocated a memory. 注意这里仅仅为在 json 中存在的 data allocate memory.
package main

import (
“encoding/json”
“fmt”
“os”
)

type FamilyMember struct {
Name string
Age int
Parents []string
}

func main() {
family := FamilyMember{“Andy”, 26, []string{“Tom”, “Lucy”}}
b, err := json.Marshal(family)
if err != nil {
fmt.Fprintf(os.Stderr, “Failed to Marshal family!err:%s”, err)
os.Exit(1)
}

fmt.Printf(“%s\n”, b)

// 注意,此时 Parents slice 是 nil. 在 Unmarshal 时,会自动为其 allcated memory.
var output FamilyMember
decodeErr := json.Unmarshal(b, &output)
if decodeErr != nil {
fmt.Fprintf(os.Stderr, “Failed to unmarshal!err:%s”, err.Error())
os.Exit(1)
}

fmt.Printf(“%+v\n”, output)
}
对于指针也是一样的
package main

import (
“encoding/json”
“fmt”
“os”
)

type Bar int
type Foo struct {
Bar *Bar
}

func main() {
b := []byte(`{“Bar”:1234}`)
var data Foo
err := json.Unmarshal(b, &data)
if err != nil {
fmt.Fprintf(os.Stderr, “Failed to unmarshal!err:%s”, err.Error())
os.Exit(1)
}
fmt.Printf(“%+v\n”, data)
fmt.Printf(“%+v\n”, *(data.Bar))
}
输出为:
{Bar:0xc42001a120} // 注意此时的地址不为 nil 了,因为在 Unmarshal 已经为其 allocated 了 memory
1234
但是需要注意,Unmarshal 只会为 json data 匹配的 field 分配内存,对于没有匹配的,可能还是 nil. 所以对于如下的 structure,在使用之前还需要认为的判断是否为 nil.
type IncomingMessage struct {
Cmd *Command
Msg *Message
}
Streaming Encoders and Decoders
package main

import (
“encoding/json”
“log”
“os”
)

func main() {
dec := json.NewDecoder(os.Stdin)
enc := json.NewEncoder(os.Stdout)
for {
var v map[string]interface{}
if err := dec.Decode(&v); err != nil {
log.Println(err)
return
}
for k := range v {
if k != “Name” {
delete(v, k)
}
}
if err := enc.Encode(&v); err != nil {
log.Println(err)
}
}
}

退出移动版