mirror of
https://github.com/wneessen/niljson.git
synced 2024-11-23 06:00:49 +01:00
Compare commits
3 commits
25913bbf9f
...
02d6e1f130
Author | SHA1 | Date | |
---|---|---|---|
02d6e1f130 | |||
dee653ae88 | |||
05d806adff |
3 changed files with 287 additions and 6 deletions
15
README.md
15
README.md
|
@ -4,7 +4,7 @@ SPDX-FileCopyrightText: 2024 Winni Neessen <wn@neessen.dev>
|
|||
SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
|
||||
# niljson - A simple Go package for unmarshalling null-able JSON types
|
||||
# niljson - A simple Go package for (un-)marshalling null-able JSON types
|
||||
|
||||
[![GoDoc](https://godoc.org/github.com/wneessen/niljson?status.svg)](https://pkg.go.dev/github.com/wneessen/niljson)
|
||||
[![codecov](https://codecov.io/gh/wneessen/niljson/branch/main/graph/badge.svg?token=W4QI1RMR4L)](https://codecov.io/gh/wneessen/niljson)
|
||||
|
@ -12,7 +12,7 @@ SPDX-License-Identifier: CC0-1.0
|
|||
[![REUSE status](https://api.reuse.software/badge/github.com/wneessen/niljson)](https://api.reuse.software/info/github.com/wneessen/niljson)
|
||||
<a href="https://ko-fi.com/D1D24V9IX"><img src="https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/5cbed8a4ae2b88347c06c923_BuyMeACoffee_blue.png" height="20" alt="buy ma a coffee"></a>
|
||||
|
||||
niljson provides a simple and efficient way to handle nullable JSON fields during the unmarshalling process.
|
||||
niljson provides a simple and efficient way to handle nullable JSON fields during the (un-)marshalling process.
|
||||
In JSON, it's common to encounter fields that can be `null`, but handling these fields in Go can be cumbersome,
|
||||
especially when dealing with primitive types like `int`, `float64`, `bool`. These types can all be either `0` (as value)
|
||||
or `null`. In Go you can always work with pointers but these, of course, can lead to unhandled nil pointer dereferences.
|
||||
|
@ -25,9 +25,7 @@ checks for `nil` values.
|
|||
|
||||
- **Nullable Types**: Provides a range of nullable types (`NilString`, `NilInt`, `NilFloat`, `NilBool`, etc.) that
|
||||
are easy to use and integrate into your existing Go structs.
|
||||
- **Seamless Integration**: These types work just like Go's standard types but add support for `null` values,
|
||||
enabling cleaner and more maintainable code.
|
||||
- **JSON Unmarshalling Support**: Automatically handles the unmarshalling of JSON fields, converting `null` JSON
|
||||
- **JSON Unmarshalling Support**: Automatically handles the (un-)marshalling of JSON fields, converting `null` JSON
|
||||
values to Go's `nil` or zero values, depending on the context.
|
||||
- **Minimalistic and Lightweight**: Designed to be lightweight and unobtrusive, so it won't bloat your application
|
||||
or introduce unnecessary dependencies (only relies on the Go standard library)
|
||||
|
@ -87,5 +85,12 @@ func main() {
|
|||
output += fmt.Sprintf("String is: %s", example.String.Value())
|
||||
}
|
||||
fmt.Println(output)
|
||||
|
||||
data, err := json.Marshal(&example)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to marshal JSON: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(data)
|
||||
}
|
||||
```
|
||||
|
|
18
niljson.go
18
niljson.go
|
@ -36,6 +36,14 @@ func (v *Variable[T]) Reset() {
|
|||
v.notNil = false
|
||||
}
|
||||
|
||||
// NewVariable returns a new Variable of generic type
|
||||
func NewVariable[T any](value T) Variable[T] {
|
||||
return Variable[T]{
|
||||
notNil: true,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// NilBoolean is an boolean type that can be nil
|
||||
type NilBoolean = Variable[bool]
|
||||
|
||||
|
@ -72,7 +80,7 @@ type NilFloat64 = Variable[float64]
|
|||
// NilString is a string type that can be nil
|
||||
type NilString = Variable[string]
|
||||
|
||||
// UnmarshalJSON interprets the generic Nil types and sets the value and notnil of the type
|
||||
// UnmarshalJSON satisfies the json.Unmarshaler interface for generic Variable types
|
||||
func (v *Variable[T]) UnmarshalJSON(data []byte) error {
|
||||
if string(data) != "null" {
|
||||
v.value = *new(T)
|
||||
|
@ -81,3 +89,11 @@ func (v *Variable[T]) UnmarshalJSON(data []byte) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON satisfies the json.Marshaler interface for generic Variable types
|
||||
func (v *Variable[T]) MarshalJSON() ([]byte, error) {
|
||||
if !v.notNil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
return json.Marshal(v.value)
|
||||
}
|
||||
|
|
260
niljson_test.go
260
niljson_test.go
|
@ -56,6 +56,25 @@ func TestVariable_UnmarshalJSON_Boolean(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVariable_MarshalJSON_Boolean(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilBoolean `json:"bool"`
|
||||
NilValue NilBoolean `json:"nilvalue,omitempty"`
|
||||
}
|
||||
|
||||
expected := `{"bool":false,"nilvalue":null}`
|
||||
jt := &JSONType{
|
||||
Value: NewVariable(false),
|
||||
}
|
||||
data, err := json.Marshal(&jt)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal json with nil types: %s", err)
|
||||
}
|
||||
if !bytes.Equal(data, []byte(expected)) {
|
||||
t.Errorf("expected json to be %q, got %q", expected, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariable_UnmarshalJSON_ByteSlice(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilByteSlice `json:"bytes"`
|
||||
|
@ -83,6 +102,25 @@ func TestVariable_UnmarshalJSON_ByteSlice(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVariable_MarshalJSON_ByteSlice(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilByteSlice `json:"bytes"`
|
||||
NilValue NilBoolean `json:"nilvalue,omitempty"`
|
||||
}
|
||||
|
||||
expected := `{"bytes":"Ynl0ZXM=","nilvalue":null}`
|
||||
jt := &JSONType{
|
||||
Value: NewVariable([]byte("bytes")),
|
||||
}
|
||||
data, err := json.Marshal(&jt)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal json with nil types: %s", err)
|
||||
}
|
||||
if !bytes.Equal(data, []byte(expected)) {
|
||||
t.Errorf("expected json to be %q, got %q", expected, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariable_UnmarshalJSON_Float32(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilFloat32 `json:"float32"`
|
||||
|
@ -111,6 +149,25 @@ func TestVariable_UnmarshalJSON_Float32(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVariable_MarshalJSON_Float32(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilFloat32 `json:"float32"`
|
||||
NilValue NilBoolean `json:"nilvalue,omitempty"`
|
||||
}
|
||||
|
||||
expected := `{"float32":1.234,"nilvalue":null}`
|
||||
jt := &JSONType{
|
||||
Value: NewVariable(float32(1.234)),
|
||||
}
|
||||
data, err := json.Marshal(&jt)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal json with nil types: %s", err)
|
||||
}
|
||||
if !bytes.Equal(data, []byte(expected)) {
|
||||
t.Errorf("expected json to be %q, got %q", expected, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariable_UnmarshalJSON_Float64(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilFloat64 `json:"float64"`
|
||||
|
@ -139,6 +196,25 @@ func TestVariable_UnmarshalJSON_Float64(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVariable_MarshalJSON_Float64(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilFloat64 `json:"float64"`
|
||||
NilValue NilBoolean `json:"nilvalue,omitempty"`
|
||||
}
|
||||
|
||||
expected := `{"float64":123.456,"nilvalue":null}`
|
||||
jt := &JSONType{
|
||||
Value: NewVariable(123.456),
|
||||
}
|
||||
data, err := json.Marshal(&jt)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal json with nil types: %s", err)
|
||||
}
|
||||
if !bytes.Equal(data, []byte(expected)) {
|
||||
t.Errorf("expected json to be %q, got %q", expected, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariable_UnmarshalJSON_Int(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilInt `json:"int"`
|
||||
|
@ -167,6 +243,25 @@ func TestVariable_UnmarshalJSON_Int(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVariable_MarshalJSON_Int(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilInt `json:"int"`
|
||||
NilValue NilBoolean `json:"nilvalue,omitempty"`
|
||||
}
|
||||
|
||||
expected := `{"int":123,"nilvalue":null}`
|
||||
jt := &JSONType{
|
||||
Value: NewVariable(123),
|
||||
}
|
||||
data, err := json.Marshal(&jt)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal json with nil types: %s", err)
|
||||
}
|
||||
if !bytes.Equal(data, []byte(expected)) {
|
||||
t.Errorf("expected json to be %q, got %q", expected, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariable_UnmarshalJSON_Int64(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilInt64 `json:"int64"`
|
||||
|
@ -195,6 +290,25 @@ func TestVariable_UnmarshalJSON_Int64(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVariable_MarshalJSON_Int64(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilInt64 `json:"int64"`
|
||||
NilValue NilBoolean `json:"nilvalue,omitempty"`
|
||||
}
|
||||
|
||||
expected := `{"int64":12345678901234,"nilvalue":null}`
|
||||
jt := &JSONType{
|
||||
Value: NewVariable(int64(12345678901234)),
|
||||
}
|
||||
data, err := json.Marshal(&jt)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal json with nil types: %s", err)
|
||||
}
|
||||
if !bytes.Equal(data, []byte(expected)) {
|
||||
t.Errorf("expected json to be %q, got %q", expected, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariable_UnmarshalJSON_String(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilString `json:"string"`
|
||||
|
@ -223,6 +337,25 @@ func TestVariable_UnmarshalJSON_String(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVariable_MarshalJSON_String(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilString `json:"string"`
|
||||
NilValue NilBoolean `json:"nilvalue,omitempty"`
|
||||
}
|
||||
|
||||
expected := `{"string":"test123","nilvalue":null}`
|
||||
jt := &JSONType{
|
||||
Value: NewVariable("test123"),
|
||||
}
|
||||
data, err := json.Marshal(&jt)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal json with nil types: %s", err)
|
||||
}
|
||||
if !bytes.Equal(data, []byte(expected)) {
|
||||
t.Errorf("expected json to be %q, got %q", expected, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariable_UnmarshalJSON_UInt(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilUInt `json:"uint"`
|
||||
|
@ -251,6 +384,25 @@ func TestVariable_UnmarshalJSON_UInt(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVariable_MarshalJSON_UInt(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilUInt `json:"uint"`
|
||||
NilValue NilBoolean `json:"nilvalue,omitempty"`
|
||||
}
|
||||
|
||||
expected := `{"uint":123,"nilvalue":null}`
|
||||
jt := &JSONType{
|
||||
Value: NewVariable(uint(123)),
|
||||
}
|
||||
data, err := json.Marshal(&jt)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal json with nil types: %s", err)
|
||||
}
|
||||
if !bytes.Equal(data, []byte(expected)) {
|
||||
t.Errorf("expected json to be %q, got %q", expected, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariable_UnmarshalJSON_UInt8(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilUInt8 `json:"uint8"`
|
||||
|
@ -279,6 +431,25 @@ func TestVariable_UnmarshalJSON_UInt8(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVariable_MarshalJSON_UInt8(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilUInt8 `json:"uint8"`
|
||||
NilValue NilBoolean `json:"nilvalue,omitempty"`
|
||||
}
|
||||
|
||||
expected := `{"uint8":1,"nilvalue":null}`
|
||||
jt := &JSONType{
|
||||
Value: NewVariable(uint8(1)),
|
||||
}
|
||||
data, err := json.Marshal(&jt)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal json with nil types: %s", err)
|
||||
}
|
||||
if !bytes.Equal(data, []byte(expected)) {
|
||||
t.Errorf("expected json to be %q, got %q", expected, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariable_UnmarshalJSON_UInt16(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilUInt16 `json:"uint16"`
|
||||
|
@ -307,6 +478,25 @@ func TestVariable_UnmarshalJSON_UInt16(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVariable_MarshalJSON_UInt16(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilUInt16 `json:"uint16"`
|
||||
NilValue NilBoolean `json:"nilvalue,omitempty"`
|
||||
}
|
||||
|
||||
expected := `{"uint16":2,"nilvalue":null}`
|
||||
jt := &JSONType{
|
||||
Value: NewVariable(uint16(2)),
|
||||
}
|
||||
data, err := json.Marshal(&jt)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal json with nil types: %s", err)
|
||||
}
|
||||
if !bytes.Equal(data, []byte(expected)) {
|
||||
t.Errorf("expected json to be %q, got %q", expected, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariable_UnmarshalJSON_UInt32(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilUInt32 `json:"uint32"`
|
||||
|
@ -335,6 +525,25 @@ func TestVariable_UnmarshalJSON_UInt32(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVariable_MarshalJSON_UInt32(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilUInt32 `json:"uint32"`
|
||||
NilValue NilBoolean `json:"nilvalue,omitempty"`
|
||||
}
|
||||
|
||||
expected := `{"uint32":3,"nilvalue":null}`
|
||||
jt := &JSONType{
|
||||
Value: NewVariable(uint32(3)),
|
||||
}
|
||||
data, err := json.Marshal(&jt)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal json with nil types: %s", err)
|
||||
}
|
||||
if !bytes.Equal(data, []byte(expected)) {
|
||||
t.Errorf("expected json to be %q, got %q", expected, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariable_UnmarshalJSON_UInt64(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilUInt64 `json:"uint64"`
|
||||
|
@ -363,6 +572,25 @@ func TestVariable_UnmarshalJSON_UInt64(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVariable_MarshalJSON_UInt64(t *testing.T) {
|
||||
type JSONType struct {
|
||||
Value NilUInt64 `json:"uint64"`
|
||||
NilValue NilBoolean `json:"nilvalue,omitempty"`
|
||||
}
|
||||
|
||||
expected := `{"uint64":4,"nilvalue":null}`
|
||||
jt := &JSONType{
|
||||
Value: NewVariable(uint64(4)),
|
||||
}
|
||||
data, err := json.Marshal(&jt)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal json with nil types: %s", err)
|
||||
}
|
||||
if !bytes.Equal(data, []byte(expected)) {
|
||||
t.Errorf("expected json to be %q, got %q", expected, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleVariable_UnmarshalJSON() {
|
||||
type JSONType struct {
|
||||
Bool NilBoolean `json:"bool"`
|
||||
|
@ -407,3 +635,35 @@ func ExampleVariable_UnmarshalJSON() {
|
|||
fmt.Println(output)
|
||||
// Output: Bool is: true, Float 32 is nil, Float 64 is: 0.000000, String is: test
|
||||
}
|
||||
|
||||
func ExampleVariable_MarshalJSON() {
|
||||
type JSONType struct {
|
||||
Bool NilBoolean `json:"bool"`
|
||||
ByteSlice NilByteSlice `json:"bytes"`
|
||||
Float32 NilFloat32 `json:"float32,omitempty"`
|
||||
Float64 NilFloat64 `json:"float64"`
|
||||
Int NilInt `json:"int"`
|
||||
Int64 NilInt64 `json:"int64"`
|
||||
NullString NilString `json:"nilvalue,omitempty"`
|
||||
String NilString `json:"string"`
|
||||
UInt NilUInt `json:"uint"`
|
||||
UInt8 NilUInt8 `json:"uint8"`
|
||||
}
|
||||
|
||||
example := &JSONType{
|
||||
Bool: NewVariable(false),
|
||||
ByteSlice: NewVariable([]byte("bytes")),
|
||||
Float64: NewVariable(123.456),
|
||||
Int: NewVariable(123),
|
||||
Int64: NewVariable(int64(12345678901234)),
|
||||
String: NewVariable("test"),
|
||||
UInt: NewVariable(uint(123)),
|
||||
}
|
||||
data, err := json.Marshal(example)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to marshal JSON: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(string(data))
|
||||
// Output: {"bool":false,"bytes":"Ynl0ZXM=","float32":null,"float64":123.456,"int":123,"int64":12345678901234,"nilvalue":null,"string":"test","uint":123,"uint8":null}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue