Anyone who has used PHP in JSON in Go knows that it is quite convenient for PHP to process JSON data. the json_encode and json_decode functions get everything done. So what should I do with JSON in Go?
I. encoding/json standard library
To learn the json library, you should first learn about struct tag and reflect in Go.
1. Overview
The json package implements the encoding and decoding of json objects. See RFC 4627. For the ing relationship between Json objects and go types, see the document of Marshal and Unmarshal functions.
See "JSON and Go" for an introduction to this package: http://golang.org/doc/articles/json_and_go.htmlthis is an official article.
2. core functions and types
We pay attention to the frequently used functions and types.
1) Marshal and Unmarshal
These two are the most commonly used functions, namely the encoding and decoding of json objects. These two functions have a long document that explains in detail the Go ing between the Go type and the json object. The ing relationships are organized as follows:
Bool, for JSON booleans
Float64, for JSON numbers
String, for JSON strings
[] Interface {}, for JSON arrays
Map [string] interface {}, for JSON objects
Nil for JSON null
The detailed encoding and decoding rules are explained in detail in this document. Here are several key points:
① By default, resolution is performed based on the mappings mentioned above;
② If the object implements json. when the Marshaler/Unmarshaler interface is not a nil pointer, the corresponding method is called for encoding and decoding. if this interface is not implemented, encoding is implemented. textMarshaler/TextUnmarshaler interface, the corresponding method of this interface is called for encoding and decoding;
③ In struct, the related codec is controlled through the "json" tag, which is illustrated in the following example;
④ The anonymous field of struct, which is expanded by default. you can specify the tag so that it is not expanded;
⑤ If an anonymous field exists and the same field name at the same level does not conflict with each other, the specific processing rule documentation provides instructions;
⑥ During decoding to struct, redundant or non-existent fields (including non-exported fields) will be ignored and no error will be reported;
Note that the second parameter passed to Unmarshal must be a pointer.
Example:
Package mainimport ("encoding/json" "fmt") func main () {type Book struct {Name string Price float64 // 'json: "price, string "'} var person = struct {Name string Age int Book} {Name:" polaris ", Age: 30, Book: Book {Price: 3.4, Name: "Go language" ,},} buf, _: = json. marshal (person) fmt. println (string (buf) // Output: {"Name": "polaris", "Age": 30, "Price ": 3.4} // The Name in the Book is ignored}
If you do not want to expand the embedded type, add the tag:
var person = struct { Name string Age int Book `json:"Book"`}
Sometimes, for example, if it was previously written in PHP (weak language), the value of Age may be in the format of "Age": "30". now it is implemented using Go to ensure compatibility; or a floating-point value such as Price returned to the client may involve precision issues. The client simply displays the value and returns the string of the floating-point value. In this case, you only need to add the tag 'json: ", string. Here, the comma "string" is the tag option.
If you want to ignore a field, add 'json: "-" '. if the value is null, add omitempty option, for example, 'json: ", omitempty "'
During decoding, the tag of the Field exported by struct is matched first, followed by Field, and finally various formats of Field are case insensitive, such as Name, matching NAME/NAme, etc.
2) MarshalIndent function
The functions of this function are the same as those of Marshal, but json is formatted to facilitate manual reading. For example, if the above example uses this function, MarshalIndent (person, "", "\ t") outputs the following:
{ "Name": "polaris", "Age": 30, "Price": 3.4}
3) Encoder and Decoder
Sometimes, we may directly read json from an input stream such as Request for parsing or directly output the encoded json. for convenience, the standard library provides us with the Decoder and Encoder types. They are instantiated through an io. Reader and io. Writer respectively, and read or write data from them.
By reading the source code, we can find that Encoder. encode/Decoder. the implementation of Decode and Marshal/Unmarshal is basically the same; there are some differences: Decoder has a method UseNumber, which functions: by default, the json number is mapped to float64 in Go. sometimes, this may cause some problems, such:
b := []byte(`{"Name":"polaris","Age":30,"Money":20.3}`)var person = make(map[string]interface{})err := json.Unmarshal(b, &person)if err != nil { log.Fatalln("json unmarshal error:", err)}age := person["Age"]log.Println(age.(int))
We want age to be int. The result is panic: interface conversion: interface is float64, not int.
Let's change to Decoder. Decode (use UseNumber) and try:
b := []byte(`{"Name":"polaris","Age":30,"Money":20.3}`)var person = make(map[string]interface{})decoder := json.NewDecoder(bytes.NewReader(b))decoder.UseNumber()err := decoder.Decode(&person)if err != nil { log.Fatalln("json unmarshal error:", err)}age := person["Age"]log.Println(age.(json.Number).Int64())
We use the json. Number type.
4) RawMessage type
This type is defined as type RawMessage [] byte. it can be seen that the original json object is saved. it implements the Marshaler and Unmarshaler interfaces and can delay json decoding. For examples, see the example on http://docs.studygolang.com/pkg/encoding/json/?rawmessage.
5) other functions or types are not commonly used (such as error types). If you do not describe them here, you can directly refer to the official documentation.
II. problems in Practical application
When the communication between the client and the server uses the json data format, on the one hand, we will decode the json data of the client, on the other hand, we need to encode the data in json format and send it to the client.
Generally, the json data sent by the server to the client is encoded by struct, [] struct, or map [string] interface. In addition to the preceding description, you may need to use string tag options for the value type. the Time type (which implements the Marshaler interface). the default encoding format is RFC3339, that is, 2006-01-02T15: 04: 05Z07: 00. in many cases, the client may not want this Time, more often, they only need a readable time string, such as 15:04:05. For this purpose, we can define our own type OftenTime:
func (self OftenTime) MarshalJSON() ([]byte, error) { t := time.Time(self) if y := t.Year(); y < 0 || y >= 10000 { return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]") } return []byte(t.Format("`2006-01-02 15:04:05"`)), nil}func (this *OftenTime) UnmarshalJSON(data []byte) (err error) { t := time.Time(*this) return t.UnmarshalJSON(data)}
In addition, there is a pitfall where the key of the json object must be a string, so the map [int] interface {} will report an error during encoding. the error is json. UnsupportedTypeError.
Many problems may occur when receiving client data and performing json decoding, especially when receiving data in multiple languages, such as PHP and Java. For example, if B: = [] byte ('{"Name": "polaris", "Age": 30, "Money": 20.3}'), PHP may pass the following information: b: = [] byte ('{"Name": "polaris", "Age": "30”," Money ":" 20.3 ″}'), when receiving data using struct, for Age, if it is int, we can directly define it as int type, but if it is string, it can be received through string tag options; however, if Age is sometimes int or string, a problem may occur. The ideal situation, of course, is not expected to happen, but one thing, the program must ensure that when this situation occurs, panic is not allowed.
In actual application, I encountered the above problem. Therefore, I wrote a json parsing file to support automatic type conversion. Code open source at github: https://github.com/polaris1119/jsonutils
III. performance problems
Obviously, the json codec uses the Go reflection function. Therefore, the performance is naturally not very good. because of this, open-source libraries such as ffjson and easyjson are available (on github ), they use go generate to generate code based on struct to avoid reflection. If you have high performance requirements but do not want to use msgpack/pb/thrift, you can use ffjson/easyjson to optimize the performance.