一些简单的导入导出

事先准备一个 json 文件

首先我们准备一个 json 文件,我直接就用的package.json里的一些字段

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "name": "hexo-site",
  "version": "0.0.0",
  "private": true,
  "author": "GPF <5173180@qq.com>",
  "description": "A Vue.js project",
  "dependencies": {
    "vue": "^2.2.6",
    "vue-router": "^2.3.1"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}

具体操作的代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
)

type allStruct interface{}

//定义了获取 json 的结构体
type Package struct {
	Mingzi       string `json:"name"`
	Version      string
	PrIvate      bool
	Dependencies Depend
	Browserslist []string
}
type Depend struct {
	Vue    string
	Router string `json:"vue-router"`
}

func main() {
	path := "./package.json"

	pkg := Package{}
	file, err := os.Open(path)
	checkError(err)
	defer file.Close()

	data, err := ioutil.ReadAll(file)
	checkError(err)
	//这个就是解析 json 格式内容的函数
	json.Unmarshal(data, &pkg)
	foreachStruct(pkg)
	//output
	// Mingzi -- hexo-site
	// Version -- 0.0.0
	// PrIvate -- true
	// Dependencies -- {^2.2.6 ^2.3.1}
	// Browserslist -- [> 1% last 2 versions not ie <= 8]
}

//检测错误的方法
func checkError(err error) {
	if err != nil {
		panic(err)
	}
}

//遍历结构体的方法
func foreachStruct(st interface{}) {
	t := reflect.TypeOf(st)
	v := reflect.ValueOf(st)

	for k := 0; k < t.NumField(); k++ {
		key := t.Field(k).Name
		value := v.Field(k).Interface()
		fmt.Printf("%s -- %v \n", key, value)
	}
}

这种写法是将 json 自动的赋值给定义好的 struct 上,自动赋值的条件是:

  1. 首先查找 tag 含有 jsonkey 值的可导出的 struct 字段(首字母大写),如:
1
	Mingzi       string `json:"name"`  //对应 json 中的 "name:"
  1. 其次查找字段名相同的可导出的 struct 字段 如: Version string //对应 json 中的 "version:"
  2. 然后查找可导出的大小写不敏感的字段 如: PrIvate bool //对应 json 中的 "private"

总之就是如果要求不高就建立 struct 的时候字段直接和 json 字段一一对应就好,但是一定要首字母大写

导入未知结构的 json 数据

很多时候我们需要导入的 json 的不确定内容的,或者特别特别长,比如laravel 的 composer.json文件 这个时候如果写结构体就很不灵活了,当然,像这种配置型的文件写一个长长的结构体也是可以的 于是乎我们就是用 空接口 配合 断言 来实现未知格式json 的解析 上代码: 这里的composer.json文件可以参考 laravel的composer.json文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/bitly/go-simplejson"
)

func main() {
	filePath := "./composer.json"
	file, err := os.Open(filePath)
	checkError(err)
	defer file.Close()

	data, err := ioutil.ReadAll(file)
	// checkError(err)
	readJSON(data)

	js, err := simplejson.NewJson(data)
	checkError(err)

	//获取某个字段值
	str, err := js.Get("name").String()
	checkError(err)
	fmt.Println("name -> ", str)

	//多层级的key值
	str2, err := js.Get("autoload").Get("classmap").GetIndex(0).String()
	checkError(err)
	fmt.Println("autoload.classmap[0] -> ", str2)

	//判断字段是否存在
	//源码内容 https://github.com/bitly/go-simplejson/blob/master/simplejson.go#L157
	jType, ok := js.CheckGet("type")
	if ok {
		str3, _ := jType.String()
		fmt.Println("type -> ", str3)
	} else {
		fmt.Println("no exist")
	}

	//数组
	arr, err := js.Get("keywords").Array()
	checkError(err)
	for i, v := range arr {
		fmt.Printf("arr index:%d value:%s \n", i, v)
	}

	//字典
	mp := js.Get("require").MustMap()
	fmt.Println("require's key:value is:")
	for key, value := range mp {
		fmt.Printf("%s : %s \n", key, value)
	}

}
func checkError(err error) {
	if err != nil {
		panic(err)
	}
}

func readJSON(data []byte) {
	//定义一个由空接口组成的字典用来承接解析出来的 json
	var f map[string]interface{}
	json.Unmarshal(data, &f)
	//通过断言来判断字段类型
	for k, v := range f {
		switch v.(type) {
		case string:
			fmt.Println(k, "is string ====>", v)
		case int:
			fmt.Println(k, "is int ====>", v)
		case []interface{}:
			fmt.Println(k, "is interface ====>", v)
		default:
			fmt.Println(k, "is null type ====>", v)
		}
	}
}

除了 空接口&断言 这种万用的写法,社区里也是有不少成熟的轮子,我这里用的就是一个很不错的轮子 simpleJson,以上给了几个示例,更全面的在轮子 的 github中的测试文件中列举出来了

作为新手首要的目标就是先让项目跑起来,等有一定的代码量之后再去看轮子的源码,如果先看源码经常 起到事倍功半的效果
—-这是我说的

导出 json 格式文件

知道了如何导入就要知道如何导出,接下来就是导出的一些需要注意的地方 上代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import (
	"encoding/json"
	"fmt"
)

//Server is a json format
type Server struct {
	ServerName string `json:"serverName"`
	ServerIP   string `json:"serverIP"`
	Port       int    `json:"port,string"`         // ,string 将 int,int64,bool,string 类型字段按 string 输出
	Version    string `json:"version,omitempty"`   //添加了 ,omitempty 修饰符的意思就是如果该字段未空就不输出
}

//Serverslice is a json format
type Serverslice struct {
	ID string `json:"-"` // - 将不会处理
	Servers []Server `json:"servers"`
}

func main() {
	var s Serverslice
	s.Servers = append(s.Servers, Server{"beijing_vpn", "127.0.0.1", 22, "v0.0.1"})
	s.Servers = append(s.Servers, Server{ServerName: "chegongzhuang", ServerIP: "127.0.0.2", Port: 443})

	b, err := json.Marshal(s)
	checkError(err)
	fmt.Println(string(b))
}

func checkError(err error) {
	if err != nil {
		panic(err)
	}
}


//输出内容为:
//{"servers":[{"serverName":"beijing_vpn","serverIP":"127.0.0.1","port":"22","version":"v0.0.1"},{"serverName":"chegongzhuang","serverIP":"127.0.0.2","port":"443"}]}

我们使用了encoding/json这个包,而且是将结构体转成的 json 格式,需要注意一下几点:

  1. 定义Struct的字段必须是首字母大写可导出的,如果不写tag的话将按照字段名来输出
  2. tag中设置的自定义名称如json:"serverName"将优先于字段名用于 json 的字段当中 按照我们刚才的例子就是 json 中输出 serverName 而不是 ServerName
  3. 如果 tag:"-" 将不对这个字段进行处理 marshal 函数需要注意的问题:
  4. tag:"xxx,omitempty"这种修饰词就是如果字段为空时不输出
  5. tag:"xxx,string"将 int,int64,bool,string 类型字段按 string 输出
  6. json 对象只支持 string 作为 key,因此如果要编码一个 map 必须是map[string]T (T 代表任意类型)
  7. channel,complex和 function 不能被输出
  8. 指针在编码的时候会输出指针指向的内容,空指针输出null

参照七牛的sdk 文档就是一个很好的例子