Help us shape the future of Go! Take the 2026 Go Developer Survey

nullable

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 22, 2026 License: Apache-2.0 Imports: 3 Imported by: 55

README

oapi-codegen/nullable

An implementation of a Nullable type for JSON bodies, indicating whether the field is absent, set to null, or set to a value

Unlike other known implementations, this makes it possible to both marshal and unmarshal the value, as well as represent all three states:

  • the field is not set
  • the field is explicitly set to null
  • the field is explicitly set to a given value

And can be embedded in structs, for instance with the following definition:

obj := struct {
		// RequiredID is a required, nullable field
		RequiredID     nullable.Nullable[int]     `json:"id"`
		// OptionalString is an optional, nullable field
		// NOTE that no pointer is required, only `omitempty`
		OptionalString nullable.Nullable[string] `json:"optionalString,omitempty"`
}{}

Usage

[!IMPORTANT] Although this project is under the oapi-codegen org for the oapi-codegen OpenAPI-to-Go code generator, this is intentionally released as a separate, standalone library which can be used by other projects.

First, add to your project with:

go get github.com/oapi-codegen/nullable

Check out the examples in the package documentation on pkg.go.dev for more details.

Credits

As well as contributions from:

License

Licensed under the Apache-2.0 license.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Nullable

type Nullable[T any] map[bool]T

Nullable is a generic type, which implements a field that can be one of three states:

- field is not set in the request - field is explicitly set to `null` in the request - field is explicitly set to a valid value in the request

Nullable is intended to be used with JSON marshalling and unmarshalling.

Internal implementation details:

- map[true]T means a value was provided - map[false]T means an explicit null was provided - nil or zero map means the field was not provided

If the field is expected to be optional, add the `omitempty` JSON tags. Do NOT use `*Nullable`!

Adapted from https://github.com/golang/go/issues/64515#issuecomment-1841057182

Example (MarshalOptional)
package main

import (
	"encoding/json"
	"fmt"

	"github.com/oapi-codegen/nullable"
)

func main() {
	obj := struct {
		ID nullable.Nullable[int] `json:"id,omitempty"`
	}{}

	// when it's not set (by default)
	b, err := json.Marshal(obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Unspecified:")
	fmt.Printf(`JSON: %s`+"\n", b)
	fmt.Println("---")

	// when it's not set (explicitly)
	obj.ID.SetUnspecified()

	b, err = json.Marshal(obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Unspecified:")
	fmt.Printf(`JSON: %s`+"\n", b)
	fmt.Println("---")

	// when it's set explicitly to nil
	obj.ID.SetNull()

	b, err = json.Marshal(obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Null:")
	fmt.Printf(`JSON: %s`+"\n", b)
	fmt.Println("---")

	// when it's set explicitly to the zero value
	var v int
	obj.ID.Set(v)

	b, err = json.Marshal(obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Zero value:")
	fmt.Printf(`JSON: %s`+"\n", b)
	fmt.Println("---")

	// when it's set explicitly to a specific value
	v = 12345
	obj.ID.Set(v)

	b, err = json.Marshal(obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Value:")
	fmt.Printf(`JSON: %s`+"\n", b)
	fmt.Println("---")

}
Output:
Unspecified:
JSON: {}
---
Unspecified:
JSON: {}
---
Null:
JSON: {"id":null}
---
Zero value:
JSON: {"id":0}
---
Value:
JSON: {"id":12345}
---
Example (MarshalRequired)
package main

import (
	"encoding/json"
	"fmt"

	"github.com/oapi-codegen/nullable"
)

func main() {
	obj := struct {
		ID nullable.Nullable[int] `json:"id"`
	}{}

	// when it's not set (by default)
	b, err := json.Marshal(obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Unspecified:")
	fmt.Printf(`JSON: %s`+"\n", b)
	fmt.Println("---")

	// when it's not set (explicitly)
	obj.ID.SetUnspecified()

	b, err = json.Marshal(obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Unspecified:")
	fmt.Printf(`JSON: %s`+"\n", b)
	fmt.Println("---")

	// when it's set explicitly to nil
	obj.ID.SetNull()

	b, err = json.Marshal(obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Null:")
	fmt.Printf(`JSON: %s`+"\n", b)
	fmt.Println("---")

	// when it's set explicitly to the zero value
	var v int
	obj.ID.Set(v)

	b, err = json.Marshal(obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Zero value:")
	fmt.Printf(`JSON: %s`+"\n", b)
	fmt.Println("---")

	// when it's set explicitly to a specific value
	v = 12345
	obj.ID.Set(v)

	b, err = json.Marshal(obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Value:")
	fmt.Printf(`JSON: %s`+"\n", b)
	fmt.Println("---")

}
Output:
Unspecified:
JSON: {"id":0}
---
Unspecified:
JSON: {"id":0}
---
Null:
JSON: {"id":null}
---
Zero value:
JSON: {"id":0}
---
Value:
JSON: {"id":12345}
---
Example (UnmarshalOptional)
package main

import (
	"encoding/json"
	"fmt"

	"github.com/oapi-codegen/nullable"
)

func main() {
	obj := struct {
		// Note that there is no pointer for nullable.Nullable when it's
		Name nullable.Nullable[string] `json:"name,omitempty"`
	}{}

	// when it's not set
	err := json.Unmarshal([]byte(`
		{
		}
		`), &obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Unspecified:")
	fmt.Printf("obj.Name.IsSpecified(): %v\n", obj.Name.IsSpecified())
	fmt.Printf("obj.Name.IsNull(): %v\n", obj.Name.IsNull())
	fmt.Println("---")

	// when it's set explicitly to nil
	err = json.Unmarshal([]byte(`
		{
		"name": null
		}
		`), &obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Null:")
	fmt.Printf("obj.Name.IsSpecified(): %v\n", obj.Name.IsSpecified())
	fmt.Printf("obj.Name.IsNull(): %v\n", obj.Name.IsNull())
	fmt.Println("---")

	// when it's set explicitly to the zero value
	err = json.Unmarshal([]byte(`
		{
		"name": ""
		}
		`), &obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Zero value:")
	fmt.Printf("obj.Name.IsSpecified(): %v\n", obj.Name.IsSpecified())
	fmt.Printf("obj.Name.IsNull(): %v\n", obj.Name.IsNull())
	val, err := obj.Name.Get()
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Printf("obj.Name.Get(): %#v <nil>\n", val)
	fmt.Printf("obj.Name.GetOrEmpty(): %#v\n", obj.Name.GetOrEmpty())
	fmt.Printf("obj.Name.MustGet(): %#v\n", obj.Name.MustGet())
	fmt.Println("---")

	// when it's set explicitly to a specific value
	err = json.Unmarshal([]byte(`
		{
		"name": "foo"
		}
		`), &obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Println("Value:")
	fmt.Printf("obj.Name.IsSpecified(): %v\n", obj.Name.IsSpecified())
	fmt.Printf("obj.Name.IsNull(): %v\n", obj.Name.IsNull())
	val, err = obj.Name.Get()
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Printf("obj.Name.Get(): %#v <nil>\n", val)
	fmt.Printf("obj.Name.GetOrEmpty(): %#v\n", obj.Name.GetOrEmpty())
	fmt.Printf("obj.Name.MustGet(): %#v\n", obj.Name.MustGet())
	fmt.Println("---")

}
Output:
Unspecified:
obj.Name.IsSpecified(): false
obj.Name.IsNull(): false
---
Null:
obj.Name.IsSpecified(): true
obj.Name.IsNull(): true
---
Zero value:
obj.Name.IsSpecified(): true
obj.Name.IsNull(): false
obj.Name.Get(): "" <nil>
obj.Name.GetOrEmpty(): ""
obj.Name.MustGet(): ""
---
Value:
obj.Name.IsSpecified(): true
obj.Name.IsNull(): false
obj.Name.Get(): "foo" <nil>
obj.Name.GetOrEmpty(): "foo"
obj.Name.MustGet(): "foo"
---
Example (UnmarshalRequired)
package main

import (
	"encoding/json"
	"fmt"

	"github.com/oapi-codegen/nullable"
)

func main() {
	obj := struct {
		Name nullable.Nullable[string] `json:"name"`
	}{}

	// when it's not set
	err := json.Unmarshal([]byte(`
		{
		}
		`), &obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Unspecified:")
	fmt.Printf("obj.Name.IsSpecified(): %v\n", obj.Name.IsSpecified())
	fmt.Printf("obj.Name.IsNull(): %v\n", obj.Name.IsNull())
	fmt.Println("---")

	// when it's set explicitly to nil
	err = json.Unmarshal([]byte(`
		{
		"name": null
		}
		`), &obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Null:")
	fmt.Printf("obj.Name.IsSpecified(): %v\n", obj.Name.IsSpecified())
	fmt.Printf("obj.Name.IsNull(): %v\n", obj.Name.IsNull())
	fmt.Println("---")

	// when it's set explicitly to the zero value
	err = json.Unmarshal([]byte(`
		{
		"name": ""
		}
		`), &obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Zero value:")
	fmt.Printf("obj.Name.IsSpecified(): %v\n", obj.Name.IsSpecified())
	fmt.Printf("obj.Name.IsNull(): %v\n", obj.Name.IsNull())
	val, err := obj.Name.Get()
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Printf("obj.Name.Get(): %#v <nil>\n", val)
	fmt.Printf("obj.Name.GetOrEmpty(): %#v\n", obj.Name.GetOrEmpty())
	fmt.Printf("obj.Name.MustGet(): %#v\n", obj.Name.MustGet())
	fmt.Println("---")

	// when it's set explicitly to a specific value
	err = json.Unmarshal([]byte(`
		{
		"name": "foo"
		}
		`), &obj)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Println("Value:")
	fmt.Printf("obj.Name.IsSpecified(): %v\n", obj.Name.IsSpecified())
	fmt.Printf("obj.Name.IsNull(): %v\n", obj.Name.IsNull())
	val, err = obj.Name.Get()
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Printf("obj.Name.Get(): %#v <nil>\n", val)
	fmt.Printf("obj.Name.GetOrEmpty(): %#v\n", obj.Name.GetOrEmpty())
	fmt.Printf("obj.Name.MustGet(): %#v\n", obj.Name.MustGet())
	fmt.Println("---")

}
Output:
Unspecified:
obj.Name.IsSpecified(): false
obj.Name.IsNull(): false
---
Null:
obj.Name.IsSpecified(): true
obj.Name.IsNull(): true
---
Zero value:
obj.Name.IsSpecified(): true
obj.Name.IsNull(): false
obj.Name.Get(): "" <nil>
obj.Name.GetOrEmpty(): ""
obj.Name.MustGet(): ""
---
Value:
obj.Name.IsSpecified(): true
obj.Name.IsNull(): false
obj.Name.Get(): "foo" <nil>
obj.Name.GetOrEmpty(): "foo"
obj.Name.MustGet(): "foo"
---

func NewNullNullable

func NewNullNullable[T any]() Nullable[T]

NewNullNullable is a convenience helper to allow constructing a `Nullable` with an explicit `null`, for instance to construct a field inside a struct, without introducing an intermediate variable

Example
package main

import (
	"fmt"

	"github.com/oapi-codegen/nullable"
)

func main() {
	p := struct {
		N nullable.Nullable[int]
	}{}

	p.N = nullable.NewNullNullable[int]()

	fmt.Printf("Specified: %v\n", p.N.IsSpecified())
	fmt.Printf("Null: %v\n", p.N.IsNull())
}
Output:
Specified: true
Null: true

func NewNullableWithValue

func NewNullableWithValue[T any](t T) Nullable[T]

NewNullableWithValue is a convenience helper to allow constructing a `Nullable` with a given value, for instance to construct a field inside a struct, without introducing an intermediate variable

Example
package main

import (
	"fmt"

	"github.com/oapi-codegen/nullable"
)

func main() {
	p := struct {
		N nullable.Nullable[int]
	}{}

	p.N = nullable.NewNullableWithValue(123)

	fmt.Println(p.N.Get())
}
Output:
123 <nil>

func (Nullable[T]) Get

func (t Nullable[T]) Get() (T, error)

Get retrieves the underlying value, if present, and returns an error if the value was not present

func (Nullable[T]) GetOrEmpty added in v1.2.0

func (t Nullable[T]) GetOrEmpty() T

Dig retrieves the underlying value or returns empty value if not present or was `null`. Use Get to distinguish between these cases.

func (Nullable[T]) IsNull

func (t Nullable[T]) IsNull() bool

IsNull indicate whether the field was sent, and had a value of `null`

func (Nullable[T]) IsSpecified

func (t Nullable[T]) IsSpecified() bool

IsSpecified indicates whether the field was sent

func (Nullable[T]) MarshalJSON

func (t Nullable[T]) MarshalJSON() ([]byte, error)

func (Nullable[T]) MustGet added in v1.1.0

func (t Nullable[T]) MustGet() T

func (*Nullable[T]) Set

func (t *Nullable[T]) Set(value T)

Set sets the underlying value to a given value

func (*Nullable[T]) SetNull

func (t *Nullable[T]) SetNull()

SetNull indicate that the field was sent, and had a value of `null`

func (*Nullable[T]) SetUnspecified

func (t *Nullable[T]) SetUnspecified()

SetUnspecified indicate whether the field was sent

func (*Nullable[T]) UnmarshalJSON

func (t *Nullable[T]) UnmarshalJSON(data []byte) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
⌂ Proxy