Giter VIP home page Giter VIP logo

gonull's People

Contributors

abrar71 avatar apatniv avatar bouroo avatar lukagiorgadze avatar thenixan avatar wazazaby avatar zekth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

gonull's Issues

Marshalled JSON includes attributes with `Present: false`

In the provided code, when unmarshalling the JSON the Weight attribute is correctly set to present: false, valid: false as "Weight" isn't specified in the JSON.

However, when marshalling the struct back to JSON the output includes the Weight attribute with a value of null. This is unexpected as I would assume Present: false would omit the attribute from the marshalled JSON (and that the marshalled JSON should match the original JSON).

Steps to Reproduce

  1. Run the following code: (modified from this example in the project README.md)
package main

import (
	"encoding/json"
	"fmt"

	"github.com/LukaGiorgadze/gonull"
)

type MyCustomInt int
type MyCustomFloat32 float32

type Person struct {
	Name    string
	Age     gonull.Nullable[MyCustomInt]     // present: true,  valid: true
	Address gonull.Nullable[string]          // present: true,  valid: false
	Height  gonull.Nullable[MyCustomFloat32] // present: true,  valid: false
	Weight  gonull.Nullable[string]          // present: false, valid: false
}

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))
}

Expected Behavior

The marshalled JSON should not include the Weight attribute as it was not present in the original JSON (Present: false):

{
  "Name": "Alice",
  "Age": 15,
  "Address": null,
  "Height": null
}

Actual Behavior

The marshalled JSON includes the Weight attribute with a value of null:

{
  "Name": "Alice",
  "Age": 15,
  "Address": null,
  "Height": null,
  "Weight": null // <-- not expected, this should not be included
}

Environment

Additional Context

This issue was discovered while testing the unmarshalling and marshalling of JSON data with optional fields in the Go Playground. The expected behavior is based on the assumption that Present: false should result in the attribute being omitted from the marshalled JSON.

Handling non present field in struct

This is an annoying JSON x Golang edge case but found a solution for this

Let's assume we have a struct like so:

type testStruct struct {
	Foo gonull.Nullable[*string] `json:"foo"`
}

we want to be able to handle those cases:

  • {"foo":"f"}
  • {}
  • {"foo": null}

In the current api we cannot check if the field is present. As {} and {"foo":null} will result with Valid: false and nil value. However we can do this:

diff --git a/gonull.go b/gonull.go
index b20a29e..deab37d 100644
--- a/gonull.go
+++ b/gonull.go
@@ -19,8 +19,9 @@ var (
 // It keeps track of the value (Val) and a flag (Valid) indicating whether the value has been set.
 // This allows for better handling of nullable values, ensuring proper value management and serialization.
 type Nullable[T any] struct {
-       Val   T
-       Valid bool
+       Val     T
+       Valid   bool
+       Present bool
 }
 
 // NewNullable creates a new Nullable with the given value and sets Valid to true.
@@ -59,6 +60,7 @@ func (n Nullable[T]) Value() (driver.Value, error) {
 // UnmarshalJSON implements the json.Unmarshaler interface for Nullable, allowing it to be used as a nullable field in JSON operations.
 // This method ensures proper unmarshalling of JSON data into the Nullable value, correctly setting the Valid flag based on the JSON data.
 func (n *Nullable[T]) UnmarshalJSON(data []byte) error {
+       n.Present = true
        if string(data) == "null" {
                n.Valid = false
                return nil

Then we have this successful test:

func TestPresent(t *testing.T) {
	var nullable1 testStruct
	var nullable2 testStruct
	var nullable3 testStruct

	err := json.Unmarshal([]byte(`{"foo":"f"}`), &nullable1)
	assert.NoError(t, err)
	assert.Equal(t, true, nullable1.Foo.Valid)
	assert.Equal(t, true, nullable1.Foo.Present)

	err = json.Unmarshal([]byte(`{}`), &nullable2)
	assert.NoError(t, err)
	assert.Equal(t, false, nullable2.Foo.Valid)
	assert.Equal(t, false, nullable3.Foo.Present)
	assert.Nil(t, nullable2.Foo.Val)

	err = json.Unmarshal([]byte(`{"foo": null}`), &nullable3)
	assert.NoError(t, err)
	assert.Equal(t, false, nullable3.Foo.Valid)
	assert.Equal(t, true, nullable3.Foo.Present)
	assert.Nil(t, nullable3.Foo.Val)
}

Happy to raise a PR if the implementation is ok.

Automatically convert to driver.Value supported type where possible

Hi,
First thanks for the library.
I was wondering if it would be possible convert values to driver.Value supported types where possible.
For example when wrapping a uint32 value I get a non-Value type %T returned from Value error when inserting a value into the DB.

I'm guessing this is due to the fact that in the driver.Valuer implementation the underlying value is returned as is and the standard library conversion isn't run.
https://github.com/golang/go/blob/66d34c7d08d7c536c3165dc49ed318e73ea5acc2/src/database/sql/driver/types.go#L243-L270

custom int type conversion: unsupported type

type TestEnum int

const (
    TestEnumA TestEnum = iota
    TestEnumB
)
type TestModel struct {
    ID    int
    Field gonull.Nullable[TestEnum]
}

Scan fails with the following error

sql: Scan error on column index 1, name "field": unsupported type conversion

Suggestion to add a method to return default value provided by user if Val is not Valid

Thanks for providing a useful library.

Currently, I need to do the following to determine the default value

const defaultPath = "magic.dat" // somewhere in the package

optPath := gonull.NewNullable("hello.dat")
path := ""
if optPath.Valid {
    path = optPath.Val
} else {
    path = defaultPath
}

Suggestion is to add a new receiver method OrElse so that this can be done in one line

path := optPath.OrElse(defaultPath)

Signature:

func (n *Nullable[T]) OrElse(value T) T

Add a comparison method that will compare Nullibles by value.

For example

func NullIsEqual[T comparable](a gonull.Nullable[T], b gonull.Nullable[T]) bool {
	// If both are not present, they are considered equal
	if !a.Present && !b.Present {
		return true
	}
	// If both are present and values are equal, they are considered equal
	return a.Present && b.Present && a.Val == b.Val
}

func NewNull[T comparable](value T) gonull.Nullable[T] {
	return gonull.Nullable[T](gonull.NewNullable(value))
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.