diff --git a/en/07.1.md b/en/07.1.md
index 2c00a499..17fe147c 100644
--- a/en/07.1.md
+++ b/en/07.1.md
@@ -5,19 +5,19 @@ XML is a commonly used data communication format in web services. Today, it's as
I will not make any attempts to teach XML's syntax or conventions. For that, please read more documentation about XML itself. We will only focus on how to encode and decode XML files in Go.
Suppose you work in IT, and you have to deal with the following XML configuration file:
-
-
-
-
- Shanghai_VPN
- 127.0.0.1
-
-
- Beijing_VPN
- 127.0.0.2
-
-
-
+```xml
+
+
+
+ Shanghai_VPN
+ 127.0.0.1
+
+
+ Beijing_VPN
+ 127.0.0.2
+
+
+```
The above XML document contains two kinds of information about your server: the server name and IP. We will use this document in our following examples.
## Parse XML
@@ -29,68 +29,68 @@ How do we parse this XML document? We can use the `Unmarshal` function in Go's `
the `data` parameter receives a data stream from an XML source, and `v` is the structure you want to output the parsed XML to. It is an interface, which means you can convert XML to any structure you desire. Here, we'll only talk about how to convert from XML to the `struct` type since they share similar tree structures.
Sample code:
+```Go
+package main
- package main
-
- import (
- "encoding/xml"
- "fmt"
- "io/ioutil"
- "os"
- )
-
- type Recurlyservers struct {
- XMLName xml.Name `xml:"servers"`
- Version string `xml:"version,attr"`
- Svs []server `xml:"server"`
- Description string `xml:",innerxml"`
- }
-
- type server struct {
- XMLName xml.Name `xml:"server"`
- ServerName string `xml:"serverName"`
- ServerIP string `xml:"serverIP"`
- }
-
- func main() {
- file, err := os.Open("servers.xml") // For read access.
- if err != nil {
- fmt.Printf("error: %v", err)
- return
- }
- defer file.Close()
- data, err := ioutil.ReadAll(file)
- if err != nil {
- fmt.Printf("error: %v", err)
- return
- }
- v := Recurlyservers{}
- err = xml.Unmarshal(data, &v)
- if err != nil {
- fmt.Printf("error: %v", err)
- return
- }
-
- fmt.Println(v)
- }
+import (
+ "encoding/xml"
+ "fmt"
+ "io/ioutil"
+ "os"
+)
+type Recurlyservers struct {
+ XMLName xml.Name `xml:"servers"`
+ Version string `xml:"version,attr"`
+ Svs []server `xml:"server"`
+ Description string `xml:",innerxml"`
+}
+
+type server struct {
+ XMLName xml.Name `xml:"server"`
+ ServerName string `xml:"serverName"`
+ ServerIP string `xml:"serverIP"`
+}
+
+func main() {
+ file, err := os.Open("servers.xml") // For read access.
+ if err != nil {
+ fmt.Printf("error: %v", err)
+ return
+ }
+ defer file.Close()
+ data, err := ioutil.ReadAll(file)
+ if err != nil {
+ fmt.Printf("error: %v", err)
+ return
+ }
+ v := Recurlyservers{}
+ err = xml.Unmarshal(data, &v)
+ if err != nil {
+ fmt.Printf("error: %v", err)
+ return
+ }
+
+ fmt.Println(v)
+}
+```
XML is actually a tree data structure, and we can define a very similar structure using structs in Go, then use `xml.Unmarshal` to convert from XML to our struct object. The sample code will print the following content:
-
- {{ servers} 1 [{{ server} Shanghai_VPN 127.0.0.1} {{ server} Beijing_VPN 127.0.0.2}]
-
- Shanghai_VPN
- 127.0.0.1
-
-
- Beijing_VPN
- 127.0.0.2
-
- }
-
+```xml
+{{ servers} 1 [{{ server} Shanghai_VPN 127.0.0.1} {{ server} Beijing_VPN 127.0.0.2}]
+
+ Shanghai_VPN
+ 127.0.0.1
+
+
+ Beijing_VPN
+ 127.0.0.2
+
+}
+```
We use `xml.Unmarshal` to parse the XML document to the corresponding struct object. You should see that we have something like `xml:"serverName"` in our struct. This is a feature of structs called `struct tags` for helping with reflection. Let's see the definition of `Unmarshal` again:
-
- func Unmarshal(data []byte, v interface{}) error
-
+```Go
+func Unmarshal(data []byte, v interface{}) error
+```
The first argument is an XML data stream. The second argument is storage type and supports the struct, slice and string types. Go's XML package uses reflection for data mapping, so all fields in v should be exported. However, this causes a problem: how does it know which XML field corresponds to the mapped struct field? The answer is that the XML parser parses data in a certain order. The library will try to find the matching struct tag first. If a match cannot be found then it searches through the struct field names. Be aware that all tags, field names and XML elements are case sensitive, so you have to make sure that there is a one-to-one correspondence for the mapping to succeed.
Go's reflection mechanism allows you to use this tag information to reflect XML data to a struct object. If you want to know more about reflection in Go, please read the package documentation on struct tags and reflection.
@@ -116,60 +116,60 @@ Note that all fields in structs should be exported (capitalized) in order to par
## Produce XML
What if we want to produce an XML document instead of parsing one. How do we do this in Go? Unsurprisingly, the `xml` package provides two functions which are `Marshal` and `MarshalIndent`, where the second function automatically indents the marshalled XML document. Their definition as follows:
-
- func Marshal(v interface{}) ([]byte, error)
- func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
-
+```Go
+func Marshal(v interface{}) ([]byte, error)
+func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
+```
The first argument in both of these functions is for storing a marshalled XML data stream.
Let's look at an example to see how this works:
+```Go
+package main
- package main
-
- import (
- "encoding/xml"
- "fmt"
- "os"
- )
-
- type Servers struct {
- XMLName xml.Name `xml:"servers"`
- Version string `xml:"version,attr"`
- Svs []server `xml:"server"`
- }
-
- type server struct {
- ServerName string `xml:"serverName"`
- ServerIP string `xml:"serverIP"`
- }
-
- func main() {
- v := &Servers{Version: "1"}
- v.Svs = append(v.Svs, server{"Shanghai_VPN", "127.0.0.1"})
- v.Svs = append(v.Svs, server{"Beijing_VPN", "127.0.0.2"})
- output, err := xml.MarshalIndent(v, " ", " ")
- if err != nil {
- fmt.Printf("error: %v\n", err)
- }
- os.Stdout.Write([]byte(xml.Header))
-
- os.Stdout.Write(output)
- }
+import (
+ "encoding/xml"
+ "fmt"
+ "os"
+)
+type Servers struct {
+ XMLName xml.Name `xml:"servers"`
+ Version string `xml:"version,attr"`
+ Svs []server `xml:"server"`
+}
+
+type server struct {
+ ServerName string `xml:"serverName"`
+ ServerIP string `xml:"serverIP"`
+}
+
+func main() {
+ v := &Servers{Version: "1"}
+ v.Svs = append(v.Svs, server{"Shanghai_VPN", "127.0.0.1"})
+ v.Svs = append(v.Svs, server{"Beijing_VPN", "127.0.0.2"})
+ output, err := xml.MarshalIndent(v, " ", " ")
+ if err != nil {
+ fmt.Printf("error: %v\n", err)
+ }
+ os.Stdout.Write([]byte(xml.Header))
+
+ os.Stdout.Write(output)
+}
+```
The above example prints the following information:
-
-
-
-
- Shanghai_VPN
- 127.0.0.1
-
-
- Beijing_VPN
- 127.0.0.2
-
-
-
+```xml
+
+
+
+ Shanghai_VPN
+ 127.0.0.1
+
+
+ Beijing_VPN
+ 127.0.0.2
+
+
+```
As we've previously defined, the reason we have `os.Stdout.Write([]byte(xml.Header))` is because both `xml.MarshalIndent` and `xml.Marshal` do not output XML headers on their own, so we have to explicitly print them in order to produce XML documents correctly.
Here we can see that `Marshal` also receives a v parameter of type `interface{}`. So what are the rules when marshalling to an XML document?
@@ -198,15 +198,15 @@ Then we need to figure out how to set tags in order to produce the final XML doc
- If a tag contains `",comment"`, it prints it as a comment without escaping, so you cannot have "--" in its value.
- If a tag contains `"omitempty"`, it omits this field if its value is zero-value, including false, 0, nil pointer or nil interface, zero length of array, slice, map and string.
- If a tag contains `"a>b>c"`, it prints three elements where a contains b and b contains c, like in the following code:
+```xml
+FirstName string `xml:"name>first"`
+LastName string `xml:"name>last"`
- FirstName string `xml:"name>first"`
- LastName string `xml:"name>last"`
-
-
- Asta
- Xie
-
-
+
+Asta
+Xie
+
+```
You may have noticed that struct tags are very useful for dealing with XML, and the same goes for the other data formats we'll be discussing in the following sections. If you still find that you have problems with working with struct tags, you should probably read more documentation about them before diving into the next section.
## Links
diff --git a/en/07.2.md b/en/07.2.md
index 58707d3d..8a4abc9b 100644
--- a/en/07.2.md
+++ b/en/07.2.md
@@ -7,9 +7,9 @@ The biggest difference between JSON and XML is that XML is a complete markup lan
Since JSON is becoming more and more important in web development, let's take a look at the level of support Go has for JSON. You'll find that Go's standard library has very good support for encoding and decoding JSON.
Here we use JSON to represent the example in the previous section:
-
- {"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}
-
+```json
+{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}
+```
The rest of this section will use this JSON data to introduce JSON concepts in Go.
## Parse JSON
@@ -17,34 +17,35 @@ The rest of this section will use this JSON data to introduce JSON concepts in G
### Parse to struct
Suppose we have the JSON in the above example. How can we parse this data and map it to a struct in Go? Go provides the following function for just this purpose:
-
- func Unmarshal(data []byte, v interface{}) error
-
+```Go
+func Unmarshal(data []byte, v interface{}) error
+```
We can use this function like so:
- package main
-
- import (
- "encoding/json"
- "fmt"
- )
-
- type Server struct {
- ServerName string
- ServerIP string
- }
-
- type Serverslice struct {
- Servers []Server
- }
+```Go
+package main
- func main() {
- var s Serverslice
- str := `{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}`
- json.Unmarshal([]byte(str), &s)
- fmt.Println(s)
- }
-
+import (
+ "encoding/json"
+ "fmt"
+)
+
+type Server struct {
+ ServerName string
+ ServerIP string
+}
+
+type Serverslice struct {
+ Servers []Server
+}
+
+func main() {
+ var s Serverslice
+ str := `{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}`
+ json.Unmarshal([]byte(str), &s)
+ fmt.Println(s)
+}
+```
In the above example, we defined a corresponding structs in Go for our JSON, using slice for an array of JSON objects and field name as our JSON keys. But how does Go know which JSON object corresponds to which specific struct filed? Suppose we have a key called `Foo` in JSON. How do we find its corresponding field?
- First, Go tries to find the (capitalised) exported field whose tag contains `Foo`.
@@ -65,120 +66,120 @@ We know that an interface{} can be anything in Go, so it is the best container t
- `nil` represents `JSON null`.
Suppose we have the following JSON data:
-
- b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
-
+```Go
+b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
+```
Now we parse this JSON to an interface{}:
-
- var f interface{}
- err := json.Unmarshal(b, &f)
-
+```Go
+var f interface{}
+err := json.Unmarshal(b, &f)
+```
The `f` stores a map, where keys are strings and values are interface{}'s'.
-
- f = map[string]interface{}{
- "Name": "Wednesday",
- "Age": 6,
- "Parents": []interface{}{
- "Gomez",
- "Morticia",
- },
- }
-
+```Go
+f = map[string]interface{}{
+ "Name": "Wednesday",
+ "Age": 6,
+ "Parents": []interface{}{
+ "Gomez",
+ "Morticia",
+ },
+}
+```
So, how do we access this data? Type assertion.
-
- m := f.(map[string]interface{})
-
+```Go
+m := f.(map[string]interface{})
+```
After asserted, you can use the following code to access data:
-
- for k, v := range m {
- switch vv := v.(type) {
- case string:
- fmt.Println(k, "is string", vv)
- case int:
- fmt.Println(k, "is int", vv)
- case float64:
- fmt.Println(k,"is float64",vv)
- case []interface{}:
- fmt.Println(k, "is an array:")
- for i, u := range vv {
- fmt.Println(i, u)
- }
- default:
- fmt.Println(k, "is of a type I don't know how to handle")
- }
- }
-
+```Go
+for k, v := range m {
+ switch vv := v.(type) {
+ case string:
+ fmt.Println(k, "is string", vv)
+ case int:
+ fmt.Println(k, "is int", vv)
+ case float64:
+ fmt.Println(k,"is float64",vv)
+ case []interface{}:
+ fmt.Println(k, "is an array:")
+ for i, u := range vv {
+ fmt.Println(i, u)
+ }
+ default:
+ fmt.Println(k, "is of a type I don't know how to handle")
+ }
+}
+```
As you can see, we can now parse JSON of an unknown format through interface{} and type assertion.
The above example is the official solution, but type asserting is not always convenient. So, I recommend an open source project called `simplejson`, created and maintained by bitly. Here is an example of how to use this project to deal with JSON of an unknown format:
+```Go
+js, err := NewJson([]byte(`{
+ "test": {
+ "array": [1, "2", 3],
+ "int": 10,
+ "float": 5.150,
+ "bignum": 9223372036854775807,
+ "string": "simplejson",
+ "bool": true
+ }
+}`))
- js, err := NewJson([]byte(`{
- "test": {
- "array": [1, "2", 3],
- "int": 10,
- "float": 5.150,
- "bignum": 9223372036854775807,
- "string": "simplejson",
- "bool": true
- }
- }`))
-
- arr, _ := js.Get("test").Get("array").Array()
- i, _ := js.Get("test").Get("int").Int()
- ms := js.Get("test").Get("string").MustString()
-
+arr, _ := js.Get("test").Get("array").Array()
+i, _ := js.Get("test").Get("int").Int()
+ms := js.Get("test").Get("string").MustString()
+```
It's not hard to see how convenient this is. Check out the repository to see more information: [https://github.com/bitly/go-simplejson](https://github.com/bitly/go-simplejson).
## Producing JSON
In many situations, we need to produce JSON data and respond to clients. In Go, the JSON package has a function called `Marshal` to do just that:
-
- func Marshal(v interface{}) ([]byte, error)
-
+```Go
+func Marshal(v interface{}) ([]byte, error)
+```
Suppose we need to produce a server information list. We have following sample:
+```Go
+package main
- package main
-
- import (
- "encoding/json"
- "fmt"
- )
-
- type Server struct {
- ServerName string
- ServerIP string
- }
-
- type Serverslice struct {
- Servers []Server
- }
-
- func main() {
- var s Serverslice
- s.Servers = append(s.Servers, Server{ServerName: "Shanghai_VPN", ServerIP: "127.0.0.1"})
- s.Servers = append(s.Servers, Server{ServerName: "Beijing_VPN", ServerIP: "127.0.0.2"})
- b, err := json.Marshal(s)
- if err != nil {
- fmt.Println("json err:", err)
- }
- fmt.Println(string(b))
- }
-
+import (
+ "encoding/json"
+ "fmt"
+)
+
+type Server struct {
+ ServerName string
+ ServerIP string
+}
+
+type Serverslice struct {
+ Servers []Server
+}
+
+func main() {
+ var s Serverslice
+ s.Servers = append(s.Servers, Server{ServerName: "Shanghai_VPN", ServerIP: "127.0.0.1"})
+ s.Servers = append(s.Servers, Server{ServerName: "Beijing_VPN", ServerIP: "127.0.0.2"})
+ b, err := json.Marshal(s)
+ if err != nil {
+ fmt.Println("json err:", err)
+ }
+ fmt.Println(string(b))
+}
+```
Output:
-
- {"Servers":[{"ServerName":"Shanghai_VPN","ServerIP":"127.0.0.1"},{"ServerName":"Beijing_VPN","ServerIP":"127.0.0.2"}]}
-
+```json
+{"Servers":[{"ServerName":"Shanghai_VPN","ServerIP":"127.0.0.1"},{"ServerName":"Beijing_VPN","ServerIP":"127.0.0.2"}]}
+```
As you know, all field names are capitalized, but if you want your JSON key names to start with a lower case letter, you should use `struct tag`s. Otherwise, Go will not produce data for internal fields.
+```Go
+type Server struct {
+ ServerName string `json:"serverName"`
+ ServerIP string `json:"serverIP"`
+}
- type Server struct {
- ServerName string `json:"serverName"`
- ServerIP string `json:"serverIP"`
- }
-
- type Serverslice struct {
- Servers []Server `json:"servers"`
- }
-
+type Serverslice struct {
+ Servers []Server `json:"servers"`
+}
+```
After this modification, we can produce the same JSON data as before.
Here are some points you need to keep in mind when trying to produce JSON:
@@ -189,32 +190,32 @@ Here are some points you need to keep in mind when trying to produce JSON:
- If the field type is `bool`, string, int, `int64`, etc, and its tag contains `",string"`, Go converts this field to its corresponding JSON type.
Example:
+```Go
+type Server struct {
+ // ID will not be outputed.
+ ID int `json:"-"`
- type Server struct {
- // ID will not be outputed.
- ID int `json:"-"`
-
- // ServerName2 will be converted to JSON type.
- ServerName string `json:"serverName"`
- ServerName2 string `json:"serverName2,string"`
-
- // If ServerIP is empty, it will not be outputted.
- ServerIP string `json:"serverIP,omitempty"`
- }
-
- s := Server {
- ID: 3,
- ServerName: `Go "1.0" `,
- ServerName2: `Go "1.0" `,
- ServerIP: ``,
- }
- b, _ := json.Marshal(s)
- os.Stdout.Write(b)
-
+ // ServerName2 will be converted to JSON type.
+ ServerName string `json:"serverName"`
+ ServerName2 string `json:"serverName2,string"`
+
+ // If ServerIP is empty, it will not be outputted.
+ ServerIP string `json:"serverIP,omitempty"`
+}
+
+s := Server {
+ ID: 3,
+ ServerName: `Go "1.0" `,
+ ServerName2: `Go "1.0" `,
+ ServerIP: ``,
+}
+b, _ := json.Marshal(s)
+os.Stdout.Write(b)
+```
Output:
-
- {"serverName":"Go \"1.0\" ","serverName2":"\"Go \\\"1.0\\\" \""}
-
+```json
+{"serverName":"Go \"1.0\" ","serverName2":"\"Go \\\"1.0\\\" \""}
+```
The `Marshal` function only returns data when it has succeeded, so here are some points we need to keep in mind:
- JSON only supports strings as keys, so if you want to encode a map, its type has to be `map[string]T`, where `T` is the type in Go.
diff --git a/en/07.3.md b/en/07.3.md
index 400190fd..704347cf 100644
--- a/en/07.3.md
+++ b/en/07.3.md
@@ -10,35 +10,35 @@ If you recall form validation from previous sections, we used Regexp to verify t
## Match
The `regexp` package has 3 functions to match: if it matches a pattern, then it returns true, returning false otherwise.
-
- func Match(pattern string, b []byte) (matched bool, error error)
- func MatchReader(pattern string, r io.RuneReader) (matched bool, error error)
- func MatchString(pattern string, s string) (matched bool, error error)
-
+```Go
+func Match(pattern string, b []byte) (matched bool, error error)
+func MatchReader(pattern string, r io.RuneReader) (matched bool, error error)
+func MatchString(pattern string, s string) (matched bool, error error)
+```
All 3 functions check if `pattern` matches the input source, returning true if it matches. However if your Regex has syntax errors, it will return an error. The 3 input sources of these functions are `slice of byte`, `RuneReader` and `string`.
Here is an example of how to verify an IP address:
-
- func IsIP(ip string) (b bool) {
- if m, _ := regexp.MatchString("^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$", ip); !m {
- return false
- }
- return true
+```Go
+func IsIP(ip string) (b bool) {
+ if m, _ := regexp.MatchString("^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$", ip); !m {
+ return false
}
-
+ return true
+}
+```
As you can see, using pattern in the `regexp` package is not that different. Here's one more example on verifying whether user input is valid:
-
- func main() {
- if len(os.Args) == 1 {
- fmt.Println("Usage: regexp [string]")
- os.Exit(1)
- } else if m, _ := regexp.MatchString("^[0-9]+$", os.Args[1]); m {
- fmt.Println("Number")
- } else {
- fmt.Println("Not number")
- }
+```Go
+func main() {
+ if len(os.Args) == 1 {
+ fmt.Println("Usage: regexp [string]")
+ os.Exit(1)
+ } else if m, _ := regexp.MatchString("^[0-9]+$", os.Args[1]); m {
+ fmt.Println("Number")
+ } else {
+ fmt.Println("Not number")
}
-
+}
+```
In the above examples, we use `Match(Reader|String)` to check if content is valid, but they are all easy to use.
## Filter
@@ -46,191 +46,191 @@ In the above examples, we use `Match(Reader|String)` to check if content is vali
Match mode can verify content but it cannot cut, filter or collect data from it. If you want to do that, you have to use the complex mode of Regexp.
Let's say we need to write a crawler. Here is an example for when you must use Regexp to filter and cut data.
+```Go
+package main
- package main
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "regexp"
+ "strings"
+)
- import (
- "fmt"
- "io/ioutil"
- "net/http"
- "regexp"
- "strings"
- )
-
- func main() {
- resp, err := http.Get("http://www.baidu.com")
- if err != nil {
- fmt.Println("http get error.")
- }
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- fmt.Println("http read error")
- return
- }
-
- src := string(body)
-
- // Convert HTML tags to lower case.
- re, _ := regexp.Compile("\\<[\\S\\s]+?\\>")
- src = re.ReplaceAllStringFunc(src, strings.ToLower)
-
- // Remove STYLE.
- re, _ = regexp.Compile("\\")
+ src = re.ReplaceAllString(src, "")
+
+ // Remove SCRIPT.
+ re, _ = regexp.Compile("\\