您的位置 首页 > 数码极客

json中如何传一个byte数组

前言

go是静态类型的语言

1.Go基础

1.1 Go变量声明及赋值

var name string // 声明 name = "zyj" // 赋值 var name = "zyj" // 声明并赋值 name := "zyj" // 声明并赋值

对于字符串,go中有[]byte和string两种类型,可以互相转化。

b := []byte("zyj") str := string(b)

特别注意:变量只能声明一次,即 var 和 := 均只能使用一次,而且对于同一变量二者只能选择其一。

在循环中使用 var 和 := 声明并赋值变量时,会执行多次,因为作用域是块作用域。

如果函数返回两个值,只有其中有一个未定义,就可以使用 := 已定义的会默认使用赋值。

go ""表示字符串string '' 表示字节类型 byte

1.2 循环

go语言的循环用range可以对string,array, 等进行迭代

// e.g. map的复制

for key, value := range oldMap { newMap[key] = value }

for循环这里有个小坑,例如:

for i := 0; i < len(list); i++ { // 循环退出的判断应使用定值 l.append(list, t) }

这时候,由于list数组在不断追加,所以它的长度len(list)也会一直增加,则产生与预想不同的结果。

1.3 数组

var arr [3]int // 声明 arr[0] = 1 // 设置元素值,此时数组元素为 [1, 0, 0]

声明并初始化

var arr = [3]int{1,2,3}

arr := []int{1,2,3} // 切片 或 arr := [...]int{1,2,3}

1.4 切片:TODO

arr := []int{1:1, 0:2} // {索引:值} 其余元素设置为0

注意,如果先定义再赋值,则一定要保证数组元素相等:

var a [5]int 一定对应 a = [5]int{1,2,3} var a []int 一定对应 a = []int{1,2,3} // 好像这种不定长就叫切片

也就是说go语言里面一旦定义了类型,比如数组长度,就无法在后面修改了。如果定义了一个数组后面可能会改它的长度,则使用var a[] int 来定义

1.5 切片 Slice

var arr []int // 默认定义的切片,len=0, cap=0 arr := make([]int, len, cap) // 也可以用make的方式定义切片,长度为len,容量为cap arr = append(arr, 3) // 往切片中动态添加元素 arr = append(arr, 3,5,6) // 一次增加多个

Go语言的数组在初始化后,长度和元素类型是固定的。

而切片的本质并不是数组,实际上是一个结构体,包含 指向底层数组的指针(引用),长度,和 容量

当追加操作时,切片中元素个数大于cap时,实际会重新创建一个更大的切片(该切片的cap 会比len多1),然后将原来的元素拷贝过去

1.6 结构体struct

type Boy struct { Name string Age int }

此时Boy是一个类型,可以有属于该类型的类方法:

func (b * Boy) Say() {}

创建类型实例:

var boy Boy // 声明 var boy = Boy{"zyj", 18} // 声明并赋值 b := &Boy{"zyj", 18} // 创建结构体指针 b := new(Boy) // 创建结构体指针 var p * Boy p = new(Boy) 或 p = &boy // 给指针赋值

注:如果结构体字段名开头不大写,则无法进行Marshal和Unmarshal,因为要使用外部json包,小写表示不可导出。

  • 即使Unmarshal的json序列字段小写,也可以反序列化到结构体成功。
  • 如果需要Marshal生成的json字段小写,则需要加上tag json:"name"

1.7 json序列化和反序列化:

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

Unmarshal到结构体时第二个参数 需要为 结构体指针

1.8 Map

var mymap map[string]string // 声明

此时mymap是一个空(nil)的map结构,不能往其内增加元素,否则会报错

使用make函数初始化:

mymap := make(map[string]string) // 初始化一个空的map

虽然此时map也是空,但是可以对其操作赋值, 如mymap["name"] = "zyj"

json 和 map互相转化

这样可以不需要提前定义结构体来解析json字符串

json -> map str := []byte(`{"name":"zyj","age":13}`) j(str, &mymap) // 如果map中有值,反序列化过程会在其中追加 map -> json bytes, err := j(&mymap) // 这里参数使用map或map的指针都可(map定义过即可,无须初始化)

特别的,当json字符串 Unmarshal 到 map[string]interface{} 时,会自动做以下转换:

  • 所有数值(Number)全都转换为 -> float64
  • boolean -> bool
  • String -> string
  • Array -> []interface{}
  • Object -> map[string]interface{}
  • null -> nil

同样,对于 map[string]interface{} 中的值 也需要断言后才能使用

但是如果一个json序列 Unmarshal到一个结构体,只要结构正确,再复杂也能匹配,并且数据类型无须做转化,可以直接使用。

1.9 接口 interface

type XXX interface{}

接口是什么?一个接口包含两层意思:它是一个方法的集合,同样是一个类型。让我们首先关注接口作为方法的集合这一方面。

所有实现了该接口中方法的类型(struct) 可以一同被视为属于这个接口类型

interface{} 可以接收任何类型参数,因为其内方法为空,所有类型都可以视为实现了一个空接口,所以都属于 interface{}

1.10 断言(Type Assertion)

如果函数接受interface{} 参数, 传进来一个float,此时想要对该变量进行运算,则会报错:

mismatched types interface {} and int

需要对该变量进行类型断言后才能使用:

var num interface{} num = 2.5 // 如果断言成功 val 等于 num, ok 为 true // 如果失败则 val 等于 0, ok 为 false val, ok := num.(float64) if ok { (val+1) // 输出3.5 }

精度问题:

t := 1.235

t = t+1

(t) // 打印结果 2.2359999999999998

为什么?目前只发现 1.235 和 1.236 是这样

已解决,Println不精确

应使用格式化输出: ("%f",t) // 结果:2.236000

json,map,struct转换

json Unmarshal到 interface{} 的转换

查看go语言变量类型

1.11 函数

Go 语言不像其它面向对象语言一样可以写个类,然后在类里面写一堆方法,但其实Go语言的方法很巧妙的实现了这种效果:我们只需要在普通函数前面加个接受者(receiver,写在函数名前面的括号里面),这样编译器就知道这个函数(方法)属于哪个struct了。例如:

type A struct { Name string } func (a A)foo() { //接收者写在函数名前面的括号里面 ("foo") } func main() { a := A{} a.foo() //foo }

1.12 go关键字

用于轻松开启高并发

2. Go解析json

导包: import "encoding/json"

函数:

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

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

注:Go中任何类型都是接口,interface{} 类似于C语言的void * ,用于指代任何类型

interface{} 是empty interface 任何类型都可以视为实现了空接口

注意:Go的特性,首字母为大写的字段才是可导出的字段,由于要调用json包中的函数,如果字段名小写,就无法赋值传参成功

struct结构体 和 []byte的转换:

结构体 ——> JSON byte数组

type Boy struct { Name string Age int } func main() { boy := Boy{"zyj", 23} str, _ := j(boy) (string(str)) //string() 将[]byte转化为字符串 }

输出: {"Name":"zyj","Age":23}

JSON ——> 结构体

func main() { var boy Boy str := []byte(`{"Name":"zyj","Age":23}`) err := j(str, &boy); if err != nil { (err) } (boy) }

输出: {zyj 23}

  • 结构体字段名小写也无法调用Mashal函数序列化
  • 如果实际需要的 JSON字符串 键名称是小写,最好需要在结构体中指定(使用tag的方式),这样使用结构体序列化 Mashal 的时候字段名可以转化为小写。
  • 但是如果 JSON字符串 字段名为小写,需要反序列化到结构体时,即使机构体字段大写也可以Unmashal成功,这里不区分大小写。

使用标签使序列化后字段小写

type Boy struct { Name string `json:"name"` Age int `json:"age"` }

3. go的安装编译

一般而言,go语言packge的名称和目录名保持一致。

go install用于创建应用包,生成.a文件,用于安装应用。默认是$GOPATH/pkg/darwin_amd64

go build用于编译程序,生成一个可执行文件,默认是当前目录。

go run编译并运行,注意,packge只可以是main。

可以使用go get 从开源社区,如Github 、 Googlecode 、 bitbucket 、 Launchpad获取远程包,如go get gi

go get -u 参数可以自动更新包,而且 go get 会自动获取该包依赖的其他第三方包。

go get 可以理解为以下2步:第一步是通过源码工具 clone 从互联网下载或更新指定的代码包及其依赖包到src下面;第2步执行go install对代码包及其依赖包进行编译和安装。

4. Go进阶

4.1 Go里的锁

4.1.1 Mutex 互斥锁

其中Mutex为互斥锁,Lock()加锁,Unlock()解锁,使用Lock()加锁后,便不能再次对其进行加锁,直到利用Unlock()解锁对其解锁后,才能再次加锁.适用于读写不确定场景,即读写次数没有明显的区别,并且只允许只有一个读或者写的场景,所以该锁也叫做全局锁。

package main 2 import ( 3 "sync" 4 "fmt" 5 "time" 6 ) 7 type safeCounter struct { 8 number int 9 10 } 11 func (sc *safeCounter) Increment() { 12 () 13 14 () 15 } 16 func (sc *safeCounter) Decrement() { 17 () 18 19 () 20 } 21 func (sc *safeCounter) getNumber() int { 22 () 23 number := 24 () 25 return number 26 } 27 func main() { 28 sc := new(safeCounter) 29 for i := 0; i < 100; i++ { 30 go () 31 go () 32 } 33 (1)*) 34 ()) 35 }

注意,(1)*)要加上,否则会出现混乱的结构,因为加减还没执行完毕,就打印了。

4.1.2 RWMutex读写锁

读写锁是针对读写操作的互斥锁。遵循两个原则即可

可以随便地读,并且支持多个goroutine同时读取

写的时候,既不能读也不能写。

RWMutex 提供了四个方法

func (*RWMutex) Lock

func (*RWMutex) Unlock

func (*RWMutex) RLock

func (*RWMutex) RUnlock

package main import ( "fmt" "math/rand" "sync" "time" ) type MapCounter struct { m map[int]int } func (mapCounter *MapCounter) Reader(n int) { for { ma() v := ma[rand.Intn(n)] ma() (v) (1 * ) } } func (mapCounter *MapCounter) Writer(n int) { for i := 0; i < n; i++ { ma() ma[i] = i * 10 ma() (1 * ) } } func main() { mc := MapCounter{m: make(map[int]int)} go mc.Writer(10) go mc.Reader(10) go mc.Reader(10) (15 * ) }

4.2 defer

Defer is used to ensure that a function call is performed later in a program’s execution, usually for purposes of cleanup. defer is often used where e.g. ensure and finally would be used in other languages.

4.2.1 defer执行顺序

package main import "fmt" func main() { defer goodbye() defer goodnight() ("Hello world.") } func goodnight() { ("GoodNight") } func goodbye() { ("Goodbye") }

输出:

Hello world.

GoodNight

Goodbye

defer在函数结束之前执行,后进先出。多个defer的执行顺序: Multiple defers are stacked last-in first-out so that the most recently deferred function is run first.**

4.2.2 那么defer之前就return了呢?

package main import "fmt" func main() { ("Hello world.") return defer goodbye() defer goodnight() } func goodnight() { ("GoodNight") } func goodbye() { ("Goodbye") }

输出:

Hello world.

defer是在return之前执行的。

4.2.3 defer用于关闭文件

package main import "fmt" import "os" func main() { f := createFile(".;) defer closeFile(f) writeFile(f) } func createFile(p string) *os.File { ("creating") f, err := os.Create(p) if err != nil { panic(err) } return f } func writeFile(f *os.File) { ("writing") (f, "data") } func closeFile(f *os.File) { ("closing") f.Close() }

4.2.4 defer用于释放锁

func Divide(i int) error { mu.Lock() defer mu.Unlock() if i == 0 { return errors.New("Can't divide by zero!") } val /= i return nil }

4.2.5 defer中的坑

package main import ( "fmt" ) func main() { (f()) (f1()) (f2()) } func f() (result int) { defer func() { result++ }() return 0 } func f1() (r int) { t := 5 defer func() { t = t + 5 }() return t } func f2() (r int) { defer func(r int) { r = r + 5 }(r) return 1 }

输出:

1

5

1

要使用defer时不踩坑,最重要的一点就是要明白,return xxx这一条语句并不是一条原子指令! 函数返回的过程是这样的:先给返回值赋值,然后调用defer表达式,最后才是返回到调用函数中。

责任编辑: 鲁达

1.内容基于多重复合算法人工智能语言模型创作,旨在以深度学习研究为目的传播信息知识,内容观点与本网站无关,反馈举报请
2.仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证;
3.本站属于非营利性站点无毒无广告,请读者放心使用!

“json中如何传一个byte数组”边界阅读