Comment traiter les données json en langage Go
Go json包
Marshal():Go数据对象 -> json数据
UnMarshal():Json数据 -> Go数据对象
func Marshal(v interface{}) ([]byte, error) func Unmarshal(data []byte, v interface{}) error
构建json数据
Marshal()和MarshalIndent()函数可以将数据封装成json数据。
1、struct、slice、array、map都可以转换成json
2、struct转换成json的时候,只有字段首字母大写的才会被转换
3、map转换的时候,key必须为string
4、封装的时候,如果是指针,会追踪指针指向的对象进行封装
例如:
有一个struct结构:
type Post struct { Id int Content string Author string }
这个结构表示博客文章类型,有文章ID,文章内容,文章的提交作者。这没什么可说的,唯一需要指明的是:它是一个struct,struct可以封装(编码)成JSON数据。
要将这段struct数据转换成json,只需使用Marshal()即可。如下:
post := &Post{1, "Hello World", "userA"} b, err := json.Marshal(post) if err != nil { fmt.Println(nil) }
Marshal()返回的是一个[]byte类型,现在变量b就已经存储了一段[]byte类型的json数据,可以输出它:
fmt.Println(string(b))
结果:
{"Id":1,"Content":"Hello World","Author":"userA"}
可以在封装成json的时候进行"美化",使用MarshalIndent()即可自动添加前缀(前缀字符串一般设置为空)和缩进:
c,err := json.MarshalIndent(post,"","\t") if err != nil { fmt.Println(nil) } fmt.Println(string(c))
结果:
{ "Id": 1, "Content": "Hello World", "Author": "userA" }
除了struct,array、slice、map结构都能解析成json,但是map解析成json的时候,key必须只能是string,这是json语法要求的。
例如:
// 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))
返回结果:
[ "a", "b", "c" ] { "a": "aa", "b": "bb", "c": "cc" }
使用struct tag辅助构建json
struct能被转换的字段都是首字母大写的字段,但如果想要在json中使用小写字母开头的key,可以使用struct的tag来辅助反射。
例如,Post结构增加一个首字母小写的字段createAt。
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))
结果:
{ "ID": 2, "content": "Hello World", "author": "userB", "label": [ "linux", "shell" ] }
使用struct tag的时候,几个注意点:
1、tag中标识的名称将称为json数据中key的值
2、tag可以设置为`json:"-"`来表示本字段不转换为json数据,即使这个字段名首字母大写
如果想要json key的名称为字符"-",则可以特殊处理`json:"-,"`,也就是加上一个逗号
3、如果tag中带有,omitempty选项,那么如果这个字段的值为0值,即false、0、""、nil等,这个字段将不会转换到json中
4、如果字段的类型为bool、string、int类、float类,而tag中又带有,string选项,那么这个字段的值将转换成json字符串
例如:
type Post struct { Id int `json:"ID,string"` Content string `json:"content"` Author string `json:"author"` Label []string `json:"label,omitempty"` }
解析json数据到struct(结构已知)
json数据可以解析到struct或空接口interface{}中(也可以是slice、map等)。理解了上面构建json时的tag规则,理解解析json就很简单了。
例如,下面是一段json数据:
{ "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" } ] }
分析下这段json数据:
1、顶层的大括号表示是一个匿名对象,映射到Go中是一个struct,假设这个struct名称为Post
2、顶层大括号里的都是Post结构中的字段,这些字段因为都是json数据,所以必须都首字母大写,同时设置tag,tag中的名称小写
3、其中author是一个子对象,映射到Go中是另一个struct,在Post中这个字段的名称为Author,假设名称和struct名称相同,也为Author
4、label是一个数组,映射到Go中可以是slice,也可以是array,且因为json array为空,所以Go中的slice/array类型不定,比如可以是int,可以是string,也可以是interface{},对于这里的示例来说,我们知道标签肯定是string
5、nextPost是一个子对象,映射到Go中是一个struct,但因为json中这个对象为null,表示这个对象不存在,所以无法判断映射到Go中struct的类型。但对此处的示例来说,是没有下一篇文章,所以它的类型应该也是Post类型
6、comment是子对象,且是数组包围的,映射到Go中,是一个slice/array,slice/array的类型是一个struct
分析之后,对应地去构建struct和struct的tag就很容易了。如下,是根据上面分析构建出来的数据结构:
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"` }
注意,前面在介绍构建json数据的时候说明过,指针会进行追踪,所以这里反推出来的struct中使用指针类型是没问题的。
于是,解析上面的json数据到Post类型的对象中,假设这个json数据存放在a.json文件中。代码如下:
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) }
输出结果:
{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'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'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语言教程栏目。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds

Problème de threading de file d'attente dans Go Crawler Colly explore le problème de l'utilisation de la bibliothèque Crawler Crawler dans le langage Go, les développeurs rencontrent souvent des problèmes avec les threads et les files d'attente de demande. � ...

La bibliothèque utilisée pour le fonctionnement du numéro de point flottante dans le langage go présente comment s'assurer que la précision est ...

En ce qui concerne le problème des balises de structure personnalisées dans Goland lorsque vous utilisez Goland pour le développement du langage GO, vous rencontrez souvent des problèmes de configuration. L'un d'eux est ...

Quelles bibliothèques de GO sont développées par de grandes entreprises ou des projets open source bien connus? Lors de la programmation en Go, les développeurs rencontrent souvent des besoins communs, ...

La différence entre l'impression de chaîne dans le langage go: la différence dans l'effet de l'utilisation de fonctions println et string () est en Go ...

GO POINTER SYNTAXE ET ATTENDRE DES PROBLÈMES DANS LA BIBLIOTHÈQUE VIPER Lors de la programmation en langage Go, il est crucial de comprendre la syntaxe et l'utilisation des pointeurs, en particulier dans ...

Deux façons de définir les structures dans le langage GO: la différence entre les mots clés VAR et le type. Lorsque vous définissez des structures, GO Language voit souvent deux façons d'écrire différentes: d'abord ...

Analyse des fuites de mémoire causées par des octets.
