I'm using this library to validate my Go struct.
https://pkg.go.dev/github.com/go-playground/validator/v10
How do I validate a field only if it's populated? For example, I have an optional phone number field in one of my structs. If the user has provided this value, I would like to validate it using E.164 format.
Phone string `validate:"e164"`
I searched for "optional" tag but couldn't find one.
As library documentation stated about Multiple Validators,
Multiple validators on a field will process in the order defined.
As #Flimzy described above, you can use omitempty to validate optional fields on structs, but omitempty should come first in the list. Otherwise it is validated and got an error.
Phone string `validate:"omitempty,e164"`
Simple proof of code is below, you can run on playground
package main
import (
"github.com/go-playground/validator/v10"
"log"
)
func main() {
sv := validator.New()
res := Response{}
err := sv.Struct(&res)
if err != nil {
log.Fatalln(err) // no error
}
res2 := Response2{}
err = sv.Struct(&res2)
if err != nil {
log.Fatalln(err) // error
}
}
type Response struct {
Phone string `validate:"omitempty,e164"`
}
type Response2 struct {
Phone string `validate:"e164,omitempty"`
}
Use the omitempty tag.
Phone string `validate:"e164,omitempty"`
Related
As title, since I'm a newbie to golang, I'm a little bit confused of the binding tag for some custom format.
For example,
there's a struct with a few of fields like this
type user struct {
name `json:"name" binding:"required"`
hobby `json:"name" binding:"required"`
}
and the name field is supposed to support lowercase and underscore only (e.g. john_cage, david)
but after I read the document of validator, still have no idea about that.
validator github
Is there's any good suggestion or solution for my case? Thanks in advance.
Read the document, google similar questions, try to compose the customer binding tag, etc.
binding tag is from gin, correct struct tag for validator is validate. Since there is no vaildation for snake_case, you should make your own. And don't forget to export the fields(Hobby, Name). If you not, (ex: hobby, name) validator will ignore the fields.
package main
import (
"fmt"
"strings"
"github.com/go-playground/validator/v10"
)
type user struct {
Hobby string `json:"name" validate:"required,snakecase"`
}
func main() {
v := validator.New()
_ = v.RegisterValidation("snakecase", validateSnakeCase)
correct := user{"playing_game"}
Incorrect := user{"playingGame"}
err := v.Struct(correct)
fmt.Println(err) // nil
err = v.Struct(Incorrect)
fmt.Println(err) // error
}
const allows = "abcdefghijklmnopqrstuvwxyz_"
func validateSnakeCase(fl validator.FieldLevel) bool {
str := fl.Field().String()
for i := range str {
if !strings.Contains(allows, str[i:i+1]) {
return false
}
}
return true
}
Playground
If you want to register the function via gin, check this out
I'm using GoLang Validator on a struct to validate its fields. Even though I don't add required tag, it still behaves as if it is required.
type Order struct {
// ... other fields
UserID string `json:"userId" validate:"uuid4"`
// ... other fields
}
if err = validator.New().Struct(i); err != nil {
return err
}
Output: User ID: unknown error
It is not required hence the value is zero-value but it still returns an error. Am I doing something wrong here?
You should add the omitempty validator to allow empty values. Try out the code below on Go playground.
type Order struct {
// ... other fields
UserID string `json:"omitempty,userId" validate:"omitempty,uuid4"`
// ... other fields
}
if err := validator.New().Struct(Order{}); err != nil {
return err
}
Note that to marshal the struct to JSON you also need to set the omitempty validator if you want to allow empty values...
I'm trying to parse JSON using Go. Can anyone tell me why my code is not working as expected?
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Name string
Body string
Time int64
}
type Person struct {
M Message
}
func get_content() {
body := []byte(`{"person":{"Name":"Alice","Body":"Hello","Time":1294706395881547000}}`)
var data Person
err := json.Unmarshal(body, &data)
if err != nil {
panic(err.Error())
}
fmt.Printf("%v",data.M.Name)
}
func main() {
get_content()
}
I'm expecting it to print the Name.
Go playground Code
There are two problems in the code.
The first one is what #umar-hayat mentioned above -> you are unmarshalling into the data object and you should be aiming at the data.M field.
The second problem is that your JSON's structure doesn't match your struct's structure. Your Person has a single field called M. If we want to represent this as JSON it would look like this:
{
"M": {
"Name": "Joe",
"Body": "Hi",
"time": 2600
}
}
Instead, you have a field called person in your JSON which cannot be matched to any field in your struct. The fact that it's similar to the name of the struct's type doesn't help in any way, I'm afraid.
So, you can either change your JSON and your target:
body := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
var data Person
err := json.Unmarshal(body, &data.M)
Or just your JSON:
body := []byte(`{"M":{"Name":"Alice","Body":"Hello","Time":1294706395881547000}}`)
var data Person
err := json.Unmarshal(body, &data)
But it's essential that the names of the fields in your JSON match the names of the fields in your struct. Or, as mentioned by Konstantinos, you can use tags in order to specify particular names with which your struct's fields will be represented in the JSON.
You might find this helpful: https://gobyexample.com/json
Here is how to Unmarshel JSON to the struct. you can check it on Go Playground here:
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Name string
Body string
Time int64
}
type Person struct {
M Message
}
func get_content() {
body := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
var data Person
err := json.Unmarshal(body, &data.M)
if err != nil {
panic(err.Error())
}
fmt.Printf(data.M.Name)
}
func main() {
get_content()
}
Replace data with data.M in below line.
err := json.Unmarshal(body, &data)
As long as you intent to map Json keys on structs whose fields have different names you should add tags:
type Message struct {
Name string `json:"Name"`
Body string `json:"Body"`
Time int64 `json:"Time"`
}
type Person struct {
M Message `json:"person"`
}
You can find more information here
In addition this answer explains in an nutshell the purpose of tags in go.
I'm using the json.unmarshalling function in golang to decode some JSON responses we got from the API. How do I make it handle multiple types?
The response we receive are always status code and a message, but the json field have different names. Sometimes these two fields are called code and message and sometimes they are called statuscode and description, depending on what we query.
say that we queries Apple and this is simply solved by creating an Apple type struct like this:
type Apple struct {
Code int `json:"code"`
Description string `json:"message"`
}
But when we query Peach, the json we got back is no longer code and message anymore, the field names became statuscode and description. So we will need the following:
type Peach struct {
Code int `json:"statuscode"`
Description string `json:"description"`
}
Potentially, we need to set up 50 more types and write duplicate for 50 times?? There MUST be a better way to do this. Unfortunately I'm new to Golang and don't know how polymorphism works in this language. Please help.
As far as I know , You should always decode into structs to benefit from go static types , the methods attached to that struct and perhaps be able to validate your responses with a package like validator , but you could always parse the JSON body into a map like this :
// JsonParse parses the json body of http request
func JsonParse(r *http.Request) (map[string]interface{}, error) {
// Read the r.body into a byte array
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
// Make a map of String keys and Interface Values
b := make(map[string]interface{})
// Unmarshal the body array into the map
err = json.Unmarshal(body, &b)
if err != nil {
return nil, err
}
return b, nil
}
I am writing a go client for the Flowdock API. Their API for Message has a number of attributes two of which are Event and Content
When Event = message then Content is a string. When Event = comment Content is a JSON object.
I want to defer unmarhsalling Content until it is needed. To do this I map RawContent in my struct and define a Content() method on Message to return the correct object.
Here is the code to illustrate:
package main
import (
"fmt"
"encoding/json"
)
// Message represents a Flowdock chat message.
type Message struct {
Event *string `json:"event,omitempty"`
RawContent *json.RawMessage `json:"content,omitempty"`
}
func (m *Message) Content() (content interface{}) {
// when m.Event is a message the RawContent is a string
// real code handles unmarshaling other types (removed for this example)
return string(*m.RawContent)
}
func main() {
var message = &Message{}
rawJSON := `{"event": "message", "content": "Howdy-Doo #Jackie #awesome"}`
if err := json.Unmarshal([]byte(rawJSON), &message); err != nil {
panic(err.Error())
}
event := "message"
rawMessage := json.RawMessage("Howdy-Doo #Jackie #awesome")
want := &Message{
Event: &event,
RawContent: &rawMessage,
}
fmt.Println(message.Content(), want.Content())
}
The result of running this is: http://play.golang.org/p/eds_AA6Aay
"Howdy-Doo #Jackie #awesome" Howdy-Doo #Jackie #awesome
Note: message.Content() and want.Content() are different!
At first I did not expect the quotes to be included in message but it makes sense because of how the JSON is parsed. It is just a slice of the whole rawJSON string.
So my questions are:
Should I just strip off the quotes?
If so what is the simplest way in Go to strip? maybe this: http://play.golang.org/p/kn8XKOqF0z lines(6, 19)?
Is this even the right approach to handling JSON that can have different types for the same attribute?
Here is a more complete example showing how I handle a JSON RawContent: http://play.golang.org/p/vrBJ5RYcql
Question 1:
No, you should json.Unmarshal the content also when it only contains a string. Apart from the quotes, JSON strings may also contain backslash-escaped control characters.
Question 2:
Because of the answer in Question 1, do the following for the "message" case:
var s string
if err := json.Unmarshal([]byte(*m.RawContent), &s); err != nil {
panic(err)
}
return s
Question 3:
It is a good approach to Unmarshal the event type string and use RawMessage to store the rest of the JSON until you've evaluated the type and know what kind of structure the content has.
You might want to consider also having a specific type also for just simple string contents, eg.:
type MessageContent string
This way you are free to implement methods on the type, allowing the Content method to return some other interface than just the empty interface{}.
Note:
Beware that, if you also json.Unmarshal the message string as I suggested, your Playground example will panic when trying to Unmarshal your non-quoted want.RawContent string, because it is not valid JSON.