DEV Community

Luka Giorgadze
Luka Giorgadze

Posted on

Gonull: A Go Package for Handling Nullable Values with Ease

When working with databases and JSON, developers often come across the challenge of managing nullable values. Nulls can be pesky. They can introduce unexpected errors if not handled correctly, and often require additional checks in the code. To address this, the open-source package gonull steps in as a solution.

What is gonull?

gonull provides a generic Nullable type for Go applications. Its core purpose is to simplify the process of handling nullable values, especially when dealing with databases and JSON.

Core Features:

  • Generic Nullable Type: At the heart of gonull is the Nullable type, which can hold a nullable value of any specified type. This type has two fields:

    • Val: Holds the actual value.
    • Valid: A flag indicating whether the value has been set or not.
  • Ease of Creation: You can quickly create a new Nullable with an initial value using the NewNullable function. This also sets the Valid flag to true.

  • Database Integration: gonull makes it seamless to integrate nullable values with database/sql. The Scan and Value methods enable the Nullable type to act as a nullable field in database operations.

  • JSON Operations: The package provides built-in methods (UnmarshalJSON and MarshalJSON) to handle the serialization and deserialization of nullable values when working with JSON data.

How Does it Work?

Consider a scenario where you want to represent a Person with optional fields, such as Age, Address, and Height. These fields might or might not have values, and when serialized into JSON, they should be represented correctly (either with their value or with null).

Here's a simple example using the gonull package:

package main

import (
    "encoding/json"
    "fmt"

    "github.com/lomsa-dev/gonull"
)

type MyCustomInt int
type MyCustomFloat32 float32

type Person struct {
    Name    string
    Age     gonull.Nullable[MyCustomInt]
    Address gonull.Nullable[string]
    Height  gonull.Nullable[MyCustomFloat32]
}

func main() {
    jsonData := []byte(`{"Name":"Alice","Age":15,"Address":null,"Height":null}`)

    var person Person
    err := json.Unmarshal(jsonData, &person)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Unmarshalled Person: %+v\n", person)

    marshalledData, err := json.Marshal(person)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Marshalled JSON: %s\n", string(marshalledData))
}
Enter fullscreen mode Exit fullscreen mode

In the above example, the Age, Address, and Height fields of the Person struct are of the Nullable type. When we unmarshal the JSON data, these fields correctly interpret the JSON values, including recognizing null values. When marshaled back into JSON, the fields that are unset (or not valid) are represented as null.

Top comments (1)

Collapse
 
thedenisnikulin profile image
Denis

cool, but I used to using "samber/mo".Option[T] type which essentially does the same thing + implements db scanner and json marshaler