Home  >  Article  >  Backend Development  >  How to process json data in Go language

How to process json data in Go language

尚
forward
2020-01-11 17:14:0711736browse

How to process json data in Go language

Go json package

Marshal(): Go data object-> json data

UnMarshal(): Json data-> Go data object

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

Building json data

Marshal() and MarshalIndent() functions can Encapsulate the data into json data.

1. Struct, slice, array and map can all be converted into json

2. When converting struct into json, only the first letter of the field will be converted

3. When converting map, the key must be string

4. When encapsulating, if it is a pointer, the object pointed to by the pointer will be tracked and encapsulated

For example:

There is a struct structure:

type Post struct {
    Id      int
    Content string
    Author  string
}

This structure represents the type of blog article, including article ID, article content, and the submitting author of the article. There is nothing to say about this, the only thing that needs to be specified is: it is a struct, and struct can be encapsulated (encoded) into JSON data.

To convert this struct data into json, just use Marshal(). As follows:

post := &Post{1, "Hello World", "userA"}
b, err := json.Marshal(post)
if err != nil {
    fmt.Println(nil)
}

Marshal() returns a []byte type. Now variable b has stored a piece of []byte type json data, which can be output:

fmt.Println(string(b))

Result:

{"Id":1,"Content":"Hello World","Author":"userA"}

can be "beautified" when encapsulating it into json. Use MarshalIndent() to automatically add a prefix (the prefix string is generally set to empty) and indentation:

c,err := json.MarshalIndent(post,"","\t")
if err != nil {
    fmt.Println(nil)
}
fmt.Println(string(c))

Result:

{
    "Id": 1,
    "Content": "Hello World",
    "Author": "userA"
}

In addition to struct, array, slice, and map structures can all be parsed into json. However, when map is parsed into json, the key must only be string, which is required by json syntax.

For example:

// slice -> json
s := []string{"a", "b", "c"}
d, _ := json.MarshalIndent(s, "", "\t")
fmt.Println(string(d))

// map -> json
m := map[string]string{
    "a":"aa",
    "b":"bb",
    "c":"cc",
}
e,_ := json.MarshalIndent(m,"","\t")
fmt.Println(string(e))

Return result:

[
    "a",
    "b",
    "c"
]
{
    "a": "aa",
    "b": "bb",
    "c": "cc"
}

Use struct tag to assist in building json

struct The fields that can be converted are all fields whose first letter is capitalized, but if you want to use keys starting with lowercase letters in json, you can use the tag of struct to assist reflection.

For example, the Post structure adds a field createAt with the first letter lowercase.

type Post struct {
    Id      int      `json:"ID"`
    Content string   `json:"content"`
    Author  string   `json:"author"`
    Label   []string `json:"label"`
}


postp := &Post{
    2,
    "Hello World",
    "userB",
    []string{"linux", "shell"},
    }

p, _ := json.MarshalIndent(postp, "", "\t")
fmt.Println(string(p))

Result:

{
    "ID": 2,
    "content": "Hello World",
    "author": "userB",
    "label": [
        "linux",
        "shell"
    ]
}

When using struct tag, a few points to note:

1. The name identified in the tag will be called the value of the key in the json data

2. Tag can be set to `json:"-"` to indicate that this field will not be converted to json data, even if the first letter of the field name is capitalized

If you want the name of the json key to be characters "-", you can handle `json:"-,"` specially, that is, add a comma

3. If the tag contains the omitempty option, then if the value of this field is 0, That is, false, 0, "", nil, etc., this field will not be converted to json

4. If the type of the field is bool, string, int class, float class, and the tag contains, string option, then the value of this field will be converted into a json string

For example:

type Post struct {
    Id      int      `json:"ID,string"`
    Content string   `json:"content"`
    Author  string   `json:"author"`
    Label   []string `json:"label,omitempty"`
}

Parse json data into struct (the structure is known)

Json data can be parsed into struct or empty interface interface{} (it can also be slice, map, etc.). After understanding the tag rules when constructing json above, it is very simple to understand and parse json.

For example, the following is a piece of json data:

{
    "id": 1,
    "content": "hello world",
    "author": {
        "id": 2,
        "name": "userA"
    },
    "published": true,
    "label": [],
    "nextPost": null,
    "comments": [{
            "id": 3,
            "content": "good post1",
            "author": "userB"
        },
        {
            "id": 4,
            "content": "good post2",
            "author": "userC"
        }
    ]
}

Analyze this piece of json data:

1. The top-level braces represent an anonymous object, mapped to Go. It is a struct. Assume that the name of this struct is Post

2. The fields in the top-level curly brackets are all fields in the Post structure. Since these fields are all json data, the first letter must be capitalized, and the tag must be set at the same time. The name in the tag is lowercase

3. Author is a sub-object, which is mapped to another struct in Go. The name of this field in Post is Author. Assuming that the name is the same as the struct name, it is also Author

4. Label is an array, which can be mapped to slice or array in Go. And because the json array is empty, the slice/array type in Go is variable. For example, it can be int or string. , or it can be interface{}. For the example here, we know that the label must be string

5, nextPost is a sub-object, and it is mapped to a struct in Go, but because this object in json is null means that this object does not exist, so the type mapped to the struct in Go cannot be determined. But for the example here, there is no next article, so its type should also be Post type

6. comment is a sub-object and is surrounded by an array. It is mapped to Go and is a slice/array, the type of slice/array is a struct

After analysis, it is easy to construct the struct and the tag of the struct accordingly. The following is the data structure constructed based on the above analysis:

type Post struct {
    ID        int64         `json:"id"`       
    Content   string        `json:"content"`  
    Author    Author        `json:"author"`   
    Published bool          `json:"published"`
    Label     []string      `json:"label"`    
    NextPost  *Post         `json:"nextPost"` 
    Comments  []*Comment    `json:"comments"` 
}

type Author struct {
    ID   int64  `json:"id"`  
    Name string `json:"name"`
}

type Comment struct {
    ID      int64  `json:"id"`     
    Content string `json:"content"`
    Author  string `json:"author"` 
}

Note that as explained earlier when introducing the construction of json data, pointers will be tracked, so there is no need to use pointer types in the struct deduced here. questionable.

So, parse the above json data into a Post type object, assuming that this json data is stored in a.json file. The code is as follows:

func main() {
    // 打开json文件
    fh, err := os.Open("a.json")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer fh.Close()
    // 读取json文件,保存到jsonData中
    jsonData, err := ioutil.ReadAll(fh)
    if err != nil {
        fmt.Println(err)
        return
    }
    
    var post Post
    // 解析json数据到post中
    err = json.Unmarshal(jsonData, &post)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(post)
}

Output result:

{1 hello world {2 userA} true [] <nil> [0xc042072300 0xc0420723c0]}

也许你已经感受到了,从json数据反推算struct到底有多复杂,虽然逻辑不难,但如果数据复杂一点,这是件非常恶心的事情。所以,使用别人写好的工具来自动转换吧。本文后面有推荐json到数据结构的自动转换工具。

解析json到interface(结构未知)

上面是已知json数据结构的解析方式,如果json结构是未知的或者结构可能会发生改变的情况,则解析到struct是不合理的。这时可以解析到空接口interface{}或map[string]interface{}类型上,这两种类型的结果是完全一致的。

解析到interface{}上时,Go类型和JSON类型的对应关系如下

  JSON类型             Go类型                
---------------------------------------------
JSON objects    <-->  map[string]interface{} 
JSON arrays     <-->  []interface{}          
JSON booleans   <-->  bool                   
JSON numbers    <-->  float64                
JSON strings    <-->  string                 
JSON null       <-->  nil

例如:

func main() {
    // 读取json文件
    fh, err := os.Open("a.json")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer fh.Close()
    jsonData, err := ioutil.ReadAll(fh)
    if err != nil {
        fmt.Println(err)
        return
    }
    
    // 定义空接口接收解析后的json数据
    var unknown interface{}
    // 或:map[string]interface{} 结果是完全一样的
    err = json.Unmarshal(jsonData, &unknown)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(unknown)
}

输出结果:

map[nextPost:<nil> comments:[map[id:3 content:good post1
author:userB] map[id:4 content:good post2 author:userC]]
id:1 content:hello world author:map[id:2 name:userA] published:true label:[]]

上面将输出map结构。这是显然的,因为类型对应关系中已经说明了,json object解析到Go interface的时候,对应的是map结构。如果将上面输出的结构进行一下格式化,得到的将是类似下面的结构:

map[
    nextPost:<nil>
    comments:[
        map[
            id:3
            content:good post1
            author:userB
        ]
        map[
            id:4
            content:good post2
            author:userC
        ]
    ]
    id:1
    content:hello world
    author:map[
        id:2
        name:userA
    ]
    published:true
    label:[]
]

现在,可以从这个map中去判断类型、取得对应的值。但是如何判断类型?可以使用类型断言:

func main() {
    // 读取json数据
    fh, err := os.Open("a.json")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer fh.Close()
    jsonData, err := ioutil.ReadAll(fh)
    if err != nil {
        fmt.Println(err)
        return
    }
    
    // 解析json数据到interface{}
    var unknown interface{}
    err = json.Unmarshal(jsonData, &unknown)
    if err != nil {
        fmt.Println(err)
        return
    }

    // 进行断言,并switch匹配
    m := unknown.(map[string]interface{})
    for k, v := range m {
        switch vv := v.(type) {
        case string:
            fmt.Println(k, "type: string\nvalue: ", vv)
            fmt.Println("------------------")
        case float64:
            fmt.Println(k, "type: float64\nvalue: ", vv)
            fmt.Println("------------------")
        case bool:
            fmt.Println(k, "type: bool\nvalue: ", vv)
            fmt.Println("------------------")
        case map[string]interface{}:
            fmt.Println(k, "type: map[string]interface{}\nvalue: ", vv)
            for i, j := range vv {
                fmt.Println(i,": ",j)
            }
            fmt.Println("------------------")
        case []interface{}:
            fmt.Println(k, "type: []interface{}\nvalue: ", vv)
            for key, value := range vv {
                fmt.Println(key, ": ", value)
            }
            fmt.Println("------------------")
        default:
            fmt.Println(k, "type: nil\nvalue: ", vv)
            fmt.Println("------------------")
        }
    }
}

结果如下:

comments type: []interface{}
value:  [map[id:3 content:good post1 author:userB] map[author:userC id:4 content:good post2]]
0 :  map[id:3 content:good post1 author:userB]
1 :  map[id:4 content:good post2 author:userC]
------------------
id type: float64
value:  1
------------------
content type: string
value:  hello world
------------------
author type: map[string]interface{}
value:  map[id:2 name:userA]
name :  userA
id :  2
------------------
published type: bool
value:  true
------------------
label type: []interface{}
value:  []
------------------
nextPost type: nil
value:  <nil>
------------------

可见,从interface中解析非常复杂,而且可能因为嵌套结构而导致无法正确迭代遍历。这时候,可以使用第三方包simplejson,见后文。

解析、创建json流

除了可以直接解析、创建json数据,还可以处理流式数据。

1、type Decoder解码json到Go数据结构

2、ype Encoder编码Go数据结构到json

例如:

const jsonStream = `
    {"Name": "Ed", "Text": "Knock knock."}
    {"Name": "Sam", "Text": "Who&#39;s there?"}
    {"Name": "Ed", "Text": "Go fmt."}
    {"Name": "Sam", "Text": "Go fmt who?"}
    {"Name": "Ed", "Text": "Go fmt yourself!"}
`
type Message struct {
    Name, Text string
}
dec := json.NewDecoder(strings.NewReader(jsonStream))
for {
    var m Message
    if err := dec.Decode(&m); err == io.EOF {
        break
    } else if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s: %s\n", m.Name, m.Text)
}

输出:

Ed: Knock knock.
Sam: Who&#39;s there?
Ed: Go fmt.
Sam: Go fmt who?
Ed: Go fmt yourself!

再例如,从标准输入读json数据,解码后删除名为Name的元素,最后重新编码后输出到标准输出。

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)
        }
    }
}

更多go语言知识请关注PHP中文网go语言教程栏目。

The above is the detailed content of How to process json data in Go language. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:cnblogs.com. If there is any infringement, please contact admin@php.cn delete