Long story short, a junior engineer at work asked me (a long time ago now). How to make JSON
fields required. I didn't have a good answer for him, and after some short research it turns out that there isn't a good answer for this!
I therefore wrote the following article on making a package validating JSON
input: [article/README.md](Original Article).
However, a delve into the rabbit hole later, I have made my own JSON
parser, which does support required tags. It is definitely not a finished or production ready product, so all help and feedback is extremely welcome. The original article was meant to solve a problem, whereas the JSON
parser spawned from pure curiosity.
It should be possible to substitute the std json
import with the current library, without issue.
If this is not the case, please make sure to report this as an issue here on GitHub :)
The usage is very simple. Adding the JSON
struct tag "required", will have the parser enforce this field to be present, when unmarshalling:
type User struct {
FirstName string `json:"first_name,required"`
LastName string `json:"last_name,required"`
Email string `json:"email,required"`
GitHub string `json:"github"`
LinkedIn string
}
In the above example, FirstName
, LastName
and Email
are required, where as GitHub
and LinkedIn
are not.
func main() {
var user User
if err := Unmarshal([]byte(`{}`), &user); err != nil {
panic(err) // this will return an error because of the missing required fields
}
fmt.Println(user)
}
Where as the following JSON string will parse without returning an error:
`{
"first_name": "lasse",
"last_name": "jakobsen",
"email": "lasse@jakobsen.dev"
}`
Furthermore, it is possible to implement the Required
interface, to create custom validation of a type.
type Required interface {
IsValueValid() error
}
Below is an example of implementing this interface for a type alias of string
called Email
. This will validate the string to adhere to the general structure of an e-mail. This means, that when being parsed, the json
will be ensured to not only exist, but also that it's valid as defined in the IsValidValue
function.
type Email string
func (email Email) IsValueValid() error {
matched, err := regexp.MatchString(`.+@.+\..+`, string(email))
if err != nil {
return err
}
if !matched {
return errors.New("invalid email")
}
return nil
}
Refer to samples for a more detailed example of this.
As of writing this document, this library is currently using a custom json.Marshal
and json.Encoder
. This library does not currently support required
tag checking, please show your interest, if you would like this by creating a new issue. The json.Marshal
function is compatible with the standard library functionality. Though, substantially faster:
goos: darwin
goarch: amd64
pkg: github.com/Pungyeon/required/pkg/json
BenchmarkMarshalStd-8 603121 1859 ns/op 752 B/op 12 allocs/op
BenchmarkMarshalPkg-8 729928 1581 ns/op 832 B/op 10 allocs/op
PASS
ok github.com/Pungyeon/required/pkg/json 2.445s