Learning and Communication: Go Language Technology WeChat Group

Business cooperation plus WeChat: LetsFeng

Learning and sharing: GoLand2022 Genuine Activation Code for the Family Bucket Universal Edition



Textbooks, documents to learn Go language, I strongly recommend this book


Start your Go language learning journey now! Life is too short, let's Go.

picture

picture

My first Go project needs to process a bunch of JSON test firmware and pass the JSON data as parameters to the API we built. Another team created these test firmwares to provide language-independent, predictable inputs and outputs to the API.

In strongly typed languages, JSON is often difficult to handle - JSON types are strings, numbers, dictionaries, and arrays. If your language is javascript, python, ruby ​​or PHP, a great benefit of JSON is that you don't need to think about types when parsing and encoding data.

// in PHP
$object = json_decode('{"foo":"bar"}');

// in javascript
const object = JSON.parse('{"foo":"bar"}')

In strongly typed languages, you need to define how to deal with strings, numbers, dictionaries and arrays of JSON objects.

In the Go language, you need to consider how to better convert a JSON file into Go data structures when using the built-in API.

I'm not going to delve into the complex topic of how JSON is handled in Go, I'll just list two code examples to illustrate the problem.

Parse/serialize to map[string]interface


First, look at this program

package main

import (
    "encoding/json"
    "fmt"
)


func main() {

    byt := []byte(`{
        "num":6.13,
        "strs":["a","b"],
        "obj":{"foo":{"bar":"zip","zap":6}}
    }`
)
    var dat map[string]interface{}
    if err := json.Unmarshal(byt, &dat); err != nil {
        panic(err)
    }
    fmt.Println(dat)

    num := dat["num"].(float64)
    fmt.Println(num)

    strs := dat["strs"].([]interface{})
    str1 := strs[0].(string)
    fmt.Println(str1)

    obj := dat["obj"].(map[string]interface{})
    obj2 := obj["foo"].(map[string]interface{})
    fmt.Println(obj2)

}

We deserialize (eg parse, decode, etc.) the JSON data from the byt variable into a map/dictionary object called dat.

These operations are similar to other languages, except that our input needs to be a byte array (not a string), and for each value of the dictionary there needs to be a type assertion[2] in order to read or access that value.

When we're dealing with a multi-level nested JSON object, these type assertions can get very tedious.

Parse/serialize to struct


The second treatment is as follows:

package main

import (
    "encoding/json"
    "fmt"
)

type ourData struct {
    Num   float64 `json:"num"`
    Strs []string `json:"strs"`
    Obj map[string]map[string]string `json:"obj"`
}

func main() {
    byt := []byte(`{
        "num":6.13,
        "strs":["a","b"],
        "obj":{"foo":{"bar":"zip","zap":6}}
    }`
)

    res := ourData{}
    json.Unmarshal(byt, &res)
    fmt.Println(res.Num)
    fmt.Println(res.Strs)
    fmt.Println(res.Obj)
}

We use the tag function of Go struct to deserialize the bytes in the byt variable into a specific structure ourData.

The label is the string that follows the structure member definition. Our definition is as follows:

type ourData struct {
    Num   float64 `json:"num"`
    Strs []string `json:"strs"`
    Obj map[string]map[string]string `json:"obj"`
}type ourData struct {
    Num   float64 `json:"num"`
    Strs []string `json:"strs"`
    Obj map[string]map[string]string `json:"obj"`
}

You can see the JSON tag "num" for the Num member, "strs" for the Str member, and "obj" for the Obj member.


These strings use backticks [3] to declare labels as literal strings. Instead of backticks, you can also use double quotes, but using double quotes may require some extra escaping and it will look messy.

type ourData struct {
    Num   float64 "json:\"num\""
    Strs []string "json:\"strs\""
    Obj map[string]map[string]string "json:\"obj\""
}

In the definition of struct, labels are not required. If your struct contains tags, it means that Go's reflection API[4] has access to the tag's value[5]. Packages in Go can use these tags to do certain things.

Go's encoding/json package uses these tags to determine the value of each top-level JSON member when deserializing JSON members into specific structs. In other words, when you define a struct like this:

type ourData struct {
    Num   float64   `json:"num"`
}

mean:

When using json.Unmarshal to deserialize a JSON object into this struct, take the value of its top-level num member and assign it to the struct's Num member.

This operation can make your deserialization code slightly cleaner, because the programmer does not need to explicitly call the type assertion on every member value. However, this is still not the best solution.

First - tags are only valid for top-level members - nested JSON needs to correspond to nested types (eg Obj map[string]map[string]string), so tedious operations are still not avoided.

Second - it assumes that your JSON structure will not change. If you run the above program, you will find that "zap":6 is not assigned to the Obj member. You can do this by creating the type map[string]map[string]interface{}, but again you need to make type assertions.

This was my first Go project and it made me miserable.

Fortunately, now we have a more efficient way.

SJSON and GJSON


Go's built-in JSON handling hasn't changed, but there are mature packages that aim to handle JSON more concisely and efficiently.


S JSON[6] (write JSON) and GJSON[7] (read JSON) are two packages developed by Josh Baker[8] that you can use to read and write JSON strings.


You can refer to the README for code examples - here is an example of using GJSON to get nested values ​​from a JSON string:

package main

import "github.com/tidwall/gjson"

const JSON = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`

func main({
    value := gjson.Get(json, "name.last")
    println(value.String())
}

Similarly, here's an example code that uses the value in the SJSON "set" JSON string to return the string after the setting:
package main

import "github.com/tidwall/sjson"

const JSON = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`

func main({
    value, _ := sjson.Set(json, "name.last""Anderson")
    println(value)
}

If SJSON and GJSON are not to your taste, there are [9] other [10] third-party libraries [11] that can be used to handle JSON in Go programs with a little more complexity.


Reference link: https://github.com/studygolang/GCTT

For more technical articles or video tutorials related to the Go language, please follow this official account to obtain and view, thank you for your support and trust!