分类目录归档:go语言

Go json序列化map不能使用非字符类型做为键

下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"encoding/json"
"fmt"
)

type Foo struct {
   Number int    `json:"number"`
   Title  string `json:"title"`
}

func main() {
   datas := make(map[int]Foo)

   for i := 0; i < 10; i++ {
      datas[i] = Foo{Number: 1, Title: "test"}
   }

   jsonString, _ := json.Marshal(datas)

   fmt.Println(string(jsonString))
}

运行结果啥也没输出,一个空行。

将map的键改成字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"encoding/json"
"fmt"
"strconv"
)

type Foo struct {
   Number int    `json:"number"`
   Title  string `json:"title"`
}

func main() {
   datas := make(map[string]Foo)

   for i := 0; i < 10; i++ {
      datas[strconv.Itoa(i)] = Foo{Number: 1, Title: "test"}
   }

   jsonString, _ := json.Marshal(datas)

   fmt.Println(string(jsonString))
}

输出:
{“0”:{“number”:1,”title”:”test”},”1″:{“number”:1,”title”:”test”},”2″:{“number”:1,”title”:”test”},”3″:{“number”:1,”title”:”test”},”4″:{“number”:1,”title”:”test”},”5″:{“number”:1,”title”:”test”},”6″:{“number”:1,”title”:”test”},”7″:{“number”:1,”title”:”test”},”8″:{“number”:1,”title”:”test”},”9″:{“number”:1,”title”:”test”}}

Go gob TCP客户端和服务端通信示例代码

服务端:

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
package main

import (
"encoding/gob"
"fmt"
"net"
)

type P struct {
   M, N int
}

func handleConnection(conn net.Conn) {
   //decoder只需要new一次
   dec := gob.NewDecoder(conn)
   for {
      fmt.Println("Read data")
      p := &P{}
      //读取数据Go官方包已经封装在里面了
      err := dec.Decode(p)
      if err != nil {
         fmt.Println(err.Error())
         break
      }
      fmt.Println("Received : %+v", p)
   }
}

func main() {
   fmt.Println("Start server")
   ln, err := net.Listen("tcp", ":8080")
   if err != nil {
      // handle error
   }
   for {
      conn, err := ln.Accept()
      if err != nil {
         continue
      }
      go handleConnection(conn)
   }
}

客户端:

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
package main

import (
"encoding/gob"
"fmt"
"log"
"net"
"time"
)

type P struct {
   M, N int
}

func main() {
   fmt.Println("start client")
   conn, err := net.Dial("tcp", "localhost:8080")
   if err != nil {
      log.Fatal("Connection error", err)
   }

   //encoder同样只需要new一次
   encoder := gob.NewEncoder(conn)
   for i := 0; i < 3; i++ {
      p := &P{i, i + 1}
      encoder.Encode(p)
      fmt.Println("Send : %+v", p)

      time.Sleep(time.Second * 1)
   }
   conn.Close()
   fmt.Println("done")
}

服务端输出结果:
Start server
Read data
Received : %+v &{0 1}
Read data
Received : %+v &{1 2}
Read data
Received : %+v &{2 3}
Read data
EOF

客户端的输出结果:
start client
Send : %+v &{0 1}
Send : %+v &{1 2}
Send : %+v &{2 3}
done

为整数常量加添加string方法

本文摘自 http://studygolang.com/articles/3243

如果你利用 iota 来使用自定义的整数枚举类型,务必要为其添加 String() 方法。例如,像这样(http://play.golang.org/p/V5VVFB05HB):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

type State int

const (
   Running State = iota
   Stopped
   Rebooting
   Terminated
)

func main() {
   state := Running
   fmt.Println("state: ", state)
}

输出:
state: 0

除非你回顾常量定义,否则这里的0看起来毫无意义。只需要为State类型添加String()方法就可以修复这个问题(http://play.golang.org/p/ewMKl6K302):

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
package main

import "fmt"

type State int

const (
   Running State = iota
   Stopped
   Rebooting
   Terminated
)

func (s State) String() string {
   switch s {
   case Running:
      return "Running"
   case Stopped:
      return "Stopped"
   case Rebooting:
      return "Rebooting"
   case Terminated:
      return "Terminated"
   default:
      return "Unknown"
   }
}

func main() {
   state := Running
   fmt.Println("state: ", state)
}

输出:state: Running。
显然现在看起来可读性好了很多。在你调试程序的时候,这会带来更多的便利。同时还可以在实现 MarshalJSON()、UnmarshalJSON() 这类方法的时候使用同样的手段。

Go gob

gob是go官方提供的数据序列化的工具。如果需要在不同的go程序中传输数据,或者希望将某个结构体数据保存到硬盘上,可以考虑使用gob。如果数据传输双方有一方不是使用go语言实现,则需要考虑其它RPC框架,如thrift grpc等。

示例代码:

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
package main

import (
"bytes"
"encoding/gob"
"fmt"
"log"
)

type P struct {
   X, Y, Z int
   Name    string
}

type Q struct {
   X, Y *int32
   Name string
}

func main() {
   var network bytes.Buffer        // Stand-in for a network connection
   enc := gob.NewEncoder(&network) // 用于写入数据
   dec := gob.NewDecoder(&network) // 用于读取数据

   err := enc.Encode(P{3, 4, 5, "Pythagoras"})
   if err != nil {
      log.Fatal("encode error:", err)
   }

   var q Q
   err = dec.Decode(&q)
   if err != nil {
      log.Fatal("decode error:", err)
   }
   fmt.Printf("%q: {%d,%d}\n", q.Name, *q.X, *q.Y)
}

可以看到enc写入的数据,通过dec读取后,完整的还原了数据。
如果将序列化的数据通过网络传输,就可能实现远程过程调用了。

Go IDE选择

本文介绍一下常用golang编程IDE,及如何选择。
1、sublime,装了GoSublime插件后,可以用sublime开发go程序,sublime重在小巧,适合于小型项目以及简单的日常编辑。
2、vim,装逼利器,vim熟悉的人可以用,对大多数人来说不适用。
3、LiteIDE,专门写go的IDE,但是功能比较弱,项目代码多的时候,格式化、错误提示等有时候不能工作,有时候漏洞百出。而且写web项目时,对js css html编辑并不友好,个人认为和sublime基本是一个水平的。
4、IntelliJ IDEA + go插件,这是我比较推荐的方案,IntelliJ IDEA本身作为强大IDE,有各种支持代码编写的功能,比如文件快速查找、定位、版本管理、导航等,加上go插件以后,对go方面的支持不弱于以上三种方案。

所以 推荐IntelliJ IDEA + go 作为go语言编码方案。