Start with an example
We often encounter the following struct definitions:
type Person struct { Name string `json:"name"` Age int `json:"age"` }
This struct defines a type called Person, which contains two domains Name and Age; but there is the magic json:"name" behind the domain, what is this used for? This article attempts to explain this issue.
When golang objects need to be converted with json, we often use this feature.
There are two things to note:
1. If a domain does not start with capital letters, then when converted to json, the domain is ignored.
$ cat package main import ( "fmt" "encoding/json" ) type Person struct { Name string `json:"name"` age int `json:"age"` } func main() { person := Person { "tom", 12 } if b, err := (person); err != nil { ("error: %s", ()) } else { ("value: %s", b) } } $ go build -o main $ ./main value: {"name":"tom"}
We see that after converting to a json string, name is output normally, and age is discarded because age starts with lowercase letters.
2. If the json:"name" tag is not used, the output json field name and domain name are the same.
$ cat package main import ( "fmt" "encoding/json" ) type Person struct { Name string Age int } func main() { person := Person { "tom", 12 } if b, err := (person); err != nil { ("error: %s", ()) } else { ("value: %s", b) } } $ go build -o main $ ./main value: {"Name":"tom","Age":12}
We see that the output json string uses the field name defined by struct.
To sum up, the json:"name" format string is used to guide /Unmarshal, which is used to map field names when converting between a json string and a golang object. To give another example, json string and golang domain name can be converted arbitrarily:
$ cat package main import ( "fmt" "encoding/json" ) type Person struct { Name string `json:"age"` Age int `json:"address"` } func main() { person := Person { "tom", 12 } if b, err := (person); err != nil { ("error: %s", ()) } else { ("value: %s", b) } } $ go build -o main $ ./main value: {"age":"tom","address":12}
In this example, we map Name to age and Age to address. Of course, this is a weird mapping, without any positive meaning, only negative meaning, just to illustrate that any name mapping can be performed.
If we look at the source code of the json package, I can see that there is a code that is worth reading tags in encoding/json/, encoding/json/.
tag := ("json")
In other words, this json tag is used by the same.
How do we use tags
As a previous example, Person has a domain Age. Can we limit the value of Age between 1 and 100, so that it will not be too large, otherwise this value will be meaningless.
$ cat package main import ( "fmt" "strings" "strconv" "reflect" _ "encoding/json" ) type Person struct { Name string `json:"name"` Age int `json:"age" valid:"1-100"` } func (p * Person) validation() bool { v := (*p) tag := ().Field(1).("valid") val := (1).Interface().(int) ("tag=%v, val=%v\n", tag, val) result := (tag, "-") var min, max int min, _ = (result[0]) max, _ = (result[1]) if val >= min && val <= max { return true } else { return false } } func main() { person1 := Person { "tom", 12 } if () { ("person 1: valid\n") } else { ("person 1: invalid\n") } person2 := Person { "tom", 250 } if () { ("person 2 valid\n") } else { ("person 2 invalid\n") } }
In this example, we added a validate function to Person, and validate verification is whether the age is reasonable.
This function can be extended to verify any valid domain of any struct.
$ cat package main import ( "fmt" "strings" "strconv" "reflect" _ "encoding/json" ) type Person struct { Name string `json:"name"` Age int `json:"age" valid:"1-100"` } type OtherStruct struct { Age int `valid:"20-300"` } func validateStruct(s interface{}) bool { v := (s) for i := 0; i < (); i++ { fieldTag := ().Field(i).("valid") fieldName := ().Field(i).Name fieldType := (i).Type() fieldValue := (i).Interface() if fieldTag == "" || fieldTag == "-" { continue } if fieldName == "Age" && () == "int" { val := fieldValue.(int) tmp := (fieldTag, "-") var min, max int min, _ = (tmp[0]) max, _ = (tmp[1]) if val >= min && val <= max { return true } else { return false } } } return true } func main() { person1 := Person { "tom", 12 } if validateStruct(person1) { ("person 1: valid\n") } else { ("person 1: invalid\n") } person2 := Person { "jerry", 250 } if validateStruct(person2) { ("person 2: valid\n") } else { ("person 2: invalid\n") } other1 := OtherStruct { 12 } if validateStruct(other1) { ("other 1: valid\n") } else { ("other 1: invalid\n") } other2 := OtherStruct { 250 } if validateStruct(other2) { ("other 2: valid\n") } else { ("other 2: invalid\n") } }
In this example, we define a function validateStruct, which accepts any struct as a parameter; validateStruct is the validation of all Age fields defined in the struct. If the field name is Age, the field type is int, and a valid tag is defined, then it will verify whether this valid is valid.
See the execution results:
$ go build -o main
$ ./main
person 1: valid
person 2: invalid
other 1: invalid
other 2: valid
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.