Merge commit 'b0c394513ef239e341b20b03060ec8591f2e484a' into ja

This commit is contained in:
Shin Kojima
2014-12-17 01:06:05 +09:00

View File

@@ -1,26 +1,26 @@
# 7.2 JSON
JSON(JavaScript Object Notation) is a lightweight data exchange language which is based on text description, its advantages including self-descriptive, easy to understand, etc. Even though it is a sub-set of JavaScript, JSON uses different text format to become an independent language, and has some similarities with C-family languages.
JSON (JavaScript Object Notation) is a lightweight data exchange language which is based on text description. Its advantages include being self-descriptive, easy to understand, etc. Even though it is a subset of JavaScript, JSON uses a different text format, the result being that it can be considered as an independent language. JSON bears similarity to C-family languages.
The biggest difference between JSON and XML is that XML is a complete mark language, but JSON is not. JSON is smaller and faster than XML, therefore it's much easier and quicker to parse in browsers, which is an important reason that many open platforms choose to use JSON as their data exchange interface language.
The biggest difference between JSON and XML is that XML is a complete markup language, whereas JSON is not. JSON is smaller and faster than XML, therefore it's much easier and quicker to parse in browsers, which is one of the reasons why many open platforms choose to use JSON as their data exchange interface language.
Since JSON is becoming more important in web development, let's take a look at the level of support JSON in Go. Actually, the standard library has very good support for encoding and decoding JSON.
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 previous section:
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"}]}
The rest of this section will use this JSON data to introduce you how to operate JSON in Go.
The rest of this section will use this JSON data to introduce JSON concepts in Go.
## Parse JSON
### Parse to struct
Suppose we have JSON in above example, how can we parse this data and map to struct in Go? Go has following function to do this:
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
We can use this function to achieve our goal, here is a complete example:
We can use this function like so:
package main
@@ -45,35 +45,35 @@ We can use this function to achieve our goal, here is a complete example:
fmt.Println(s)
}
In above example, we defined a corresponding struct in Go for our JSON, slice for array, field name for key in JSON, but how does Go know which JSON data is for specific struct filed? Suppose we have a key called `Foo` in JSON, how to find corresponding field?
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, try to find the exported field(capitalized) whose tag contains `Foo`.
- Then, try to find the field whose name is `Foo`.
- Finally, try to find something like `FOO` or `FoO` without case sensitive.
- First, Go tries to find the (capitalised) exported field whose tag contains `Foo`.
- If no match can be found, look for the field whose name is `Foo`.
- If there are still not matches look for something like `FOO` or `FoO`, ignoring case sensitivity.
You may notice that all fields that are going to be assigned should be exported, and Go only assigns fields that can be found at the same time, and ignores all the others. This is good because when you receive a very large JSON data but you only need some of them, you can easily discard.
You may have noticed that all fields that are going to be assigned should be exported, and Go only assigns fields that can be found, ignoring all others. This can be useful if you need to deal with large chunks of JSON data but you only a specific subset of it; the data you don't need can easily be discarded.
### Parse to interface
When we know what kind of JSON we're going to have, we parse JSON to specific struct, but what if we don't know?
When we know what kind of JSON to expect in advance, we can parse it to a specific struct. But what if we don't know?
We know that interface{} can be everything in Go, so it is the best container to save our unknown format JSON. JSON package uses `map[string]interface{}` and `[]interface{}` to save all kinds of JSON objects and array. Here is a list of mapping relation:
We know that an interface{} can be anything in Go, so it is the best container to save our JSON of unknown format. The JSON package uses `map[string]interface{}` and `[]interface{}` to save all kinds of JSON objects and arrays. Here is a list of JSON mapping relations:
- `bool` represents `JSON booleans`,
- `float64` represents `JSON numbers`,
- `string` represents `JSON strings`,
- `nil` represents `JSON null`.
Suppose we have following JSON data:
Suppose we have the following JSON data:
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
Now we parse this JSON to interface{}:
Now we parse this JSON to an interface{}:
var f interface{}
err := json.Unmarshal(b, &f)
The `f` stores a map, where keys are strings and values interface{}.
The `f` stores a map, where keys are strings and values are interface{}'s'.
f = map[string]interface{}{
"Name": "Wednesday",
@@ -84,11 +84,11 @@ The `f` stores a map, where keys are strings and values interface{}.
},
}
So, how to access these data? Type assertion.
So, how do we access this data? Type assertion.
m := f.(map[string]interface{})
After asserted, you can use following code to access data:
After asserted, you can use the following code to access data:
for k, v := range m {
switch vv := v.(type) {
@@ -108,9 +108,9 @@ After asserted, you can use following code to access data:
}
}
As you can see, we can parse unknown format JSON through interface{} and type assert now.
As you can see, we can parse JSON of an unknown format through interface{} and type assert now.
The above example is the official solution, but type assert is not always convenient, so I recommend one open source project called `simplejson` and launched by bitly. Here is an example of how to use this project to deal with unknown format JSON:
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 by bitly. Here is an example of how to use this project to deal with JSON of an unknown format:
js, err := NewJson([]byte(`{
"test": {
@@ -127,15 +127,15 @@ The above example is the official solution, but type assert is not always conven
i, _ := js.Get("test").Get("int").Int()
ms := js.Get("test").Get("string").MustString()
It's not hard to see how convenient it is, see more information: [https://github.com/bitly/go-simplejson](https://github.com/bitly/go-simplejson).
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).
## Produce JSON
## Producing JSON
In many situations, we need to produce JSON data and response to clients. In Go, JSON package has a function called `Marshal` to do this job:
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)
Suppose we need to produce server information list, we have following sample:
Suppose we need to produce a server information list. We have following sample:
package main
@@ -168,7 +168,7 @@ Output:
{"Servers":[{"ServerName":"Shanghai_VPN","ServerIP":"127.0.0.1"},{"ServerName":"Beijing_VPN","ServerIP":"127.0.0.2"}]}
As you know, all fields name are capitalized, but if you want your JSON key name start with lower case, you should use `struct tag` to do this, otherwise Go will not produce data for internal fields.
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.
type Server struct {
ServerName string `json:"serverName"`
@@ -179,14 +179,14 @@ As you know, all fields name are capitalized, but if you want your JSON key name
Servers []Server `json:"servers"`
}
After this modification, we can get same JSON data as beginning.
After this modification, we can produce the same JSON data as before.
Here are some points you need to keep in mind when you try to produce JSON:
Here are some points you need to keep in mind when trying to produce JSON:
- Field tag contains `"-"` will not be outputted.
- If tag contains customized name, Go uses this instead of field name, like `serverName` in above example.
- If tag contains `omitempty`, this field will not be outputted if it is its zero-value.
- If the field type is `bool`, string, int, `int64`, etc, and its tag contains `",string"`, Go converts this field to corresponding type in JSON.
- Field tags containing `"-"` will not be outputted.
- If a tag contains a customized name, Go uses this instead of the field name, like `serverName` in the above example.
- If a tag contains `omitempty`, this field will not be outputted if it is its zero-value.
- 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:
@@ -215,17 +215,17 @@ Output:
{"serverName":"Go \"1.0\" ","serverName2":"\"Go \\\"1.0\\\" \""}
Function `Marshal` only returns data when it was succeed, so here are some points we need to keep in mind:
The `Marshal` function only returns data when it has succeeded, so here are some points we need to keep in mind:
- JSON object only supports string as key, so if you want to encode a map, its type has to be `map[string]T`, where `T` is the type in Go.
- Type like channel, complex and function are not able to be encoded to JSON.
- Do not try to encode nested data, it led dead loop when produce JSON data.
- If the field is a pointer, Go outputs data that it points to, or outputs null if it points to nil.
- 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.
- Types like channel, complex types and functions are not able to be encoded to JSON.
- Do not try to encode cyclic data, it leads to an infinite recursion.
- If the field is a pointer, Go outputs the data that it points to, or else outputs null if it points to nil.
In this section, we introduced you how to decode and encode JSON data in Go, also one third-party project called `simplejson` which is for parsing unknown format JSON. These are all important in web development.
In this section, we introduced how to decode and encode JSON data in Go. We also looked at one third-party project called `simplejson` which is for parsing JSON or unknown format. These are all useful concepts for developping web applications in Go.
## Links
- [Directory](preface.md)
- Previous section: [XML](07.1.md)
- Next section: [Regexp](07.3.md)
- Next section: [Regexp](07.3.md)