null

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Mar 16, 2023 License: MIT Imports: 4 Imported by: 0

README

Description

Package provides nullable types based on generics. Type implements json.Marshaler and json.Unmarshaler in order to serialize/deserialize JSON, driver.Valuer and sql.Scanner in order to be compatible with sql/database package.

Examples

Base usage
package main

import (
	"encoding/json"
	"fmt"
	null "github.com/alsiberij/alsnull"
	"os"
)

func main() {
	// Default value is null,
	// so `null` will be printed
	var nullableInt64 null.Type[int64]
	_ = json.NewEncoder(os.Stdout).Encode(nullableInt64)

	// In this case we explicitly set not null value `STRING`,
	// so "STRING" will be printed
	var nullableString = null.NewTypeWithValue("STRING")
	_ = json.NewEncoder(os.Stdout).Encode(nullableString)

	// Unmarshalling nullable values not differs from unmarshaling primitives,
	// but in case of having null bytes we will get null value,
	// so the output will be {{3.14 true} {false false}}
	// indicating that float64 has not null value of 3.14 and bool is null
	var someStruct struct {
		NullableFloat64 null.Type[float64] `json:"f"`
		NullableBool64  null.Type[bool]    `json:"b"`
	}
	_ = json.Unmarshal([]byte(`{"f":3.14, "b":null}`), &someStruct)
	fmt.Println(someStruct)
}

Package is compatible with sql/database so provided nullable types can be used for either scanning as destinations, or performing query with parameters. Example:

var nullableInt64 null.Type[int64]
// ...
_ = rows.Scan(&nullableInt64)
// ...
_, _ = conn.ExecContext(context.Background(), 'DELETE FROM table WHERE id = $1', nullableInt64)

Also works with pgx.

Advanced usage

If you want to use custom package or override type marshaling/unmarshaling you must define your own CustomJsonMarshaler or CustomJsonUnmarshaler. The following example shows how to override json marshaling for time.Time:

package main

import (
	"encoding/json"
	"fmt"
	null "github.com/alsiberij/alsnull"
	"time"
)

func init() {
	// The following assignments will override json marshaling/unmarshaling for time.Time type
	// Package encoding/json is used here, but you can choose third-party ones if you need

	null.JsonMarshaler = func(value any) ([]byte, error) {
		switch V := value.(type) {
		case time.Time:
			return []byte(V.Format("\"02.01.2006\"")), nil
		default:
			return json.Marshal(value)
		}
	}

	null.JsonUnmarshaler = func(b []byte, dst any) error {
		switch V := dst.(type) {
		case *time.Time:
			t, err := time.Parse("\"02.01.2006\"", string(b))
			if err != nil {
				return err
			}
			*V = t
			return nil
		default:
			return json.Unmarshal(b, dst)
		}
	}
}

type TestStruct struct {
	NullableTime null.Type[time.Time] `json:"t"`
}

func main() {
	someStruct := TestStruct{
		NullableTime: null.NewTypeWithValue(time.Unix(1672531200, 0)),
	}

	// Json marshalling was overridden, so the result is `{"t":"01.01.2023"}`
	// Not RFC3339 which is default behavior of marshaling time.Time
	b, _ := json.Marshal(&someStruct)
	fmt.Println(string(b))

	// Json unmarshalling was also overridden so not null initial value 1672531200 will be printed
	var nextStruct TestStruct
	_ = json.Unmarshal(b, &nextStruct)
	fmt.Println(nextStruct.NullableTime.ValueOrZero().Unix())
}

Documentation

Overview

Package null provides nullable types that can be used either for json marshaling/unmarshaling (even with non-default encoding/json) or sql/database interaction

Index

Constants

This section is empty.

Variables

View Source
var (
	// JsonMarshaler is used for json marshaling. Default marshaler uses encoding/json package with no custom options.
	// Is called only when parameter value is not null
	JsonMarshaler CustomJsonMarshaler = func(value any) ([]byte, error) {
		return json.Marshal(value)
	}

	// JsonUnmarshaler is used for json unmarshaling. Default unmarshaler uses encoding/json package with no custom options.
	// Is called only when parameter b not equals to `null`
	JsonUnmarshaler CustomJsonUnmarshaler = func(b []byte, dst any) error {
		return json.Unmarshal(b, dst)
	}
)

Functions

This section is empty.

Types

type CustomJsonMarshaler

type CustomJsonMarshaler func(src any) ([]byte, error)

CustomJsonMarshaler is defined in order to allow you custom (even with not encoding/json) json marshaling

Parameter src is always one of the following types: int64, float64, bool, string, time.Time, so type switch may take place.

Default one is JsonMarshaler

Example of implementing custom marshaler:

import "encoding/json"
import "time"

func Marshaler(value any) ([]byte, error) {
	switch V := value.(type) {
	case time.Time:
		return []byte(V.Format("\"02.01.2006\"")), nil
	default:
		return json.Marshal(value)
	}
}

type CustomJsonUnmarshaler

type CustomJsonUnmarshaler func(b []byte, dst any) error

CustomJsonUnmarshaler is defined in order to allow you custom (even with not encoding/json) json unmarshaling

Parameter dst is always one of the following types: *int64, *float64, *bool, *string, *time.Time, so type switch may take place.

Default one is JsonUnmarshaler

Example of implementing custom unmarshaler:

	import "encoding/json"
	import "time"

	func Unmarshaler(b []byte, dst any) error {
 	switch V := dst.(type) {
		case *time.Time:
			t, err := time.Parse("\"02.01.2006\"", string(b))
			if err != nil {
				return err
			}
			*V = t
			return nil
		default:
			return json.Unmarshal(b, dst)
		}
	}

type SupportedTypes

type SupportedTypes interface {
	int64 | float64 | bool | string | time.Time
}

SupportedTypes provides types that can be used for generic Type

type Type

type Type[T SupportedTypes] struct {
	// contains filtered or unexported fields
}

Type represents a nullable type that can used either for json marshaling/unmarshaling or sql/database interaction

func NewType deprecated

func NewType[T SupportedTypes](value T, ok bool) Type[T]

NewType provides typed Type with given value if ok is true, otherwise value is considered as null

Deprecated: second parameter is useless in current semantics. Function will be removed in future releases. Try NewTypeWithValue instead

func NewTypeWithValue added in v0.1.0

func NewTypeWithValue[T SupportedTypes](value T) Type[T]

NewTypeWithValue provides not null value of type T

func (*Type[T]) Equal

func (s *Type[T]) Equal(v Type[T]) bool

Equal checks if v is equal to current Type. Returns true if both of Type are null or have equal not null values

func (*Type[T]) IsNull

func (s *Type[T]) IsNull() bool

IsNull returns true if value is null

func (Type[T]) MarshalJSON

func (s Type[T]) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler. Uses JsonMarshaler for marshaling not null values

func (*Type[T]) RawValue

func (s *Type[T]) RawValue() (T, bool)

RawValue returns actual value and true if Type is not null, otherwise default value for T and false

func (*Type[T]) Scan

func (s *Type[T]) Scan(src any) error

Scan implements sql.Scanner for database interacting

func (*Type[T]) SetNull

func (s *Type[T]) SetNull()

SetNull sets Type to null. Existing value will be overwritten with default one

func (*Type[T]) SetValue

func (s *Type[T]) SetValue(v T)

SetValue sets Type to not null value

func (*Type[T]) UnmarshalJSON

func (s *Type[T]) UnmarshalJSON(b []byte) error

UnmarshalJSON implements json.Unmarshaler. Uses JsonUnmarshaler for unmarshaling not null values

func (Type[T]) Value

func (s Type[T]) Value() (driver.Value, error)

Value implements driver.Valuer for database interacting

func (*Type[T]) ValueOrZero

func (s *Type[T]) ValueOrZero() T

ValueOrZero returns either actual value or default value for type T depending on whether Type is null

Jump to

Keyboard shortcuts

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