New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
serialise empty values as null
(JSON) or empty element (XML) instead of the zero value of the type
#3
base: main
Are you sure you want to change the base?
serialise empty values as null
(JSON) or empty element (XML) instead of the zero value of the type
#3
Changes from all commits
917918e
5253eac
dfb8310
b990174
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,11 @@ import ( | |
"encoding/json" | ||
"encoding/xml" | ||
"fmt" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"4d63.com/optional" | ||
) | ||
|
||
|
@@ -197,22 +200,22 @@ func Example_jsonMarshalEmpty() { | |
|
||
// Output: | ||
// { | ||
// "bool": false, | ||
// "byte": 0, | ||
// "float32": 0, | ||
// "float64": 0, | ||
// "int16": 0, | ||
// "int32": 0, | ||
// "int64": 0, | ||
// "int": 0, | ||
// "rune": 0, | ||
// "string": "", | ||
// "time": "0001-01-01T00:00:00Z", | ||
// "uint16": 0, | ||
// "uint32": 0, | ||
// "uint64": 0, | ||
// "uint": 0, | ||
// "uintptr": 0 | ||
// "bool": null, | ||
// "byte": null, | ||
// "float32": null, | ||
// "float64": null, | ||
// "int16": null, | ||
// "int32": null, | ||
// "int64": null, | ||
// "int": null, | ||
// "rune": null, | ||
// "string": null, | ||
// "time": null, | ||
// "uint16": null, | ||
// "uint32": null, | ||
// "uint64": null, | ||
// "uint": null, | ||
// "uintptr": null | ||
// } | ||
} | ||
|
||
|
@@ -277,7 +280,7 @@ func Example_jsonMarshalPresent() { | |
// } | ||
} | ||
|
||
func Example_jsonUnmarshalEmpty() { | ||
func TestExample_jsonUnmarshalEmpty(t *testing.T) { | ||
s := struct { | ||
Bool optional.Optional[bool] `json:"bool"` | ||
Byte optional.Optional[byte] `json:"byte"` | ||
|
@@ -335,6 +338,64 @@ func Example_jsonUnmarshalEmpty() { | |
// Uint64: false | ||
// Uint: false | ||
// Uintptr: false | ||
|
||
t.Run("unmarshal null values to empty", func(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you put this in a new example? Not a test please. The examples show up in docs and so help people understand how the library works. |
||
s := struct { | ||
Bool optional.Optional[bool] `json:"bool"` | ||
Byte optional.Optional[byte] `json:"byte"` | ||
Float32 optional.Optional[float32] `json:"float32"` | ||
Float64 optional.Optional[float64] `json:"float64"` | ||
Int16 optional.Optional[int16] `json:"int16"` | ||
Int32 optional.Optional[int32] `json:"int32"` | ||
Int64 optional.Optional[int64] `json:"int64"` | ||
Int optional.Optional[int] `json:"int"` | ||
Rune optional.Optional[rune] `json:"rune"` | ||
String optional.Optional[string] `json:"string"` | ||
Time optional.Optional[time.Time] `json:"time"` | ||
Uint16 optional.Optional[uint16] `json:"uint16"` | ||
Uint32 optional.Optional[uint32] `json:"uint32"` | ||
Uint64 optional.Optional[uint64] `json:"uint64"` | ||
Uint optional.Optional[uint] `json:"uint"` | ||
Uintptr optional.Optional[uintptr] `json:"uintptr"` | ||
}{} | ||
|
||
x := `{ | ||
"float64": null, | ||
"bool": null, | ||
"byte": null, | ||
"float32": null, | ||
"int16": null, | ||
"int32": null, | ||
"int64": null, | ||
"int": null, | ||
"rune": null, | ||
"string": null, | ||
"time": null, | ||
"uint16": null, | ||
"uint32": null, | ||
"uint64": null, | ||
"uint": null, | ||
"uintptr": null | ||
}` | ||
json.Unmarshal([]byte(x), &s) | ||
assert.False(t, s.Bool.IsPresent()) | ||
assert.False(t, s.Byte.IsPresent()) | ||
assert.False(t, s.Float32.IsPresent()) | ||
assert.False(t, s.Float64.IsPresent()) | ||
assert.False(t, s.Int16.IsPresent()) | ||
assert.False(t, s.Int32.IsPresent()) | ||
assert.False(t, s.Int64.IsPresent()) | ||
assert.False(t, s.Int.IsPresent()) | ||
assert.False(t, s.Rune.IsPresent()) | ||
assert.False(t, s.String.IsPresent()) | ||
assert.False(t, s.Time.IsPresent()) | ||
assert.False(t, s.Uint16.IsPresent()) | ||
assert.False(t, s.Uint32.IsPresent()) | ||
assert.False(t, s.Uint64.IsPresent()) | ||
assert.False(t, s.Uint64.IsPresent()) | ||
assert.False(t, s.Uint.IsPresent()) | ||
assert.False(t, s.Uint.IsPresent()) | ||
}) | ||
} | ||
|
||
func Example_jsonUnmarshalPresent() { | ||
|
@@ -416,7 +477,7 @@ func Example_jsonUnmarshalPresent() { | |
|
||
func Example_xmlMarshalOmitEmpty() { | ||
s := struct { | ||
XMLName xml.Name `xml:"s"` | ||
XMLName xml.Name `xml:"s"` | ||
Bool optional.Optional[bool] `xml:"bool,omitempty"` | ||
Byte optional.Optional[byte] `xml:"byte,omitempty"` | ||
Float32 optional.Optional[float32] `xml:"float32,omitempty"` | ||
|
@@ -461,7 +522,7 @@ func Example_xmlMarshalOmitEmpty() { | |
|
||
func Example_xmlMarshalEmpty() { | ||
s := struct { | ||
XMLName xml.Name `xml:"s"` | ||
XMLName xml.Name `xml:"s"` | ||
Bool optional.Optional[bool] `xml:"bool"` | ||
Byte optional.Optional[byte] `xml:"byte"` | ||
Float32 optional.Optional[float32] `xml:"float32"` | ||
|
@@ -502,28 +563,28 @@ func Example_xmlMarshalEmpty() { | |
|
||
// Output: | ||
// <s> | ||
// <bool>false</bool> | ||
// <byte>0</byte> | ||
// <float32>0</float32> | ||
// <float64>0</float64> | ||
// <int16>0</int16> | ||
// <int32>0</int32> | ||
// <int64>0</int64> | ||
// <int>0</int> | ||
// <rune>0</rune> | ||
// <bool></bool> | ||
// <byte></byte> | ||
// <float32></float32> | ||
// <float64></float64> | ||
// <int16></int16> | ||
// <int32></int32> | ||
// <int64></int64> | ||
// <int></int> | ||
// <rune></rune> | ||
// <string></string> | ||
// <time>0001-01-01T00:00:00Z</time> | ||
// <uint16>0</uint16> | ||
// <uint32>0</uint32> | ||
// <uint64>0</uint64> | ||
// <uint>0</uint> | ||
// <uintptr>0</uintptr> | ||
// <time></time> | ||
// <uint16></uint16> | ||
// <uint32></uint32> | ||
// <uint64></uint64> | ||
// <uint></uint> | ||
// <uintptr></uintptr> | ||
// </s> | ||
} | ||
|
||
func Example_xmlMarshalPresent() { | ||
s := struct { | ||
XMLName xml.Name `xml:"s"` | ||
XMLName xml.Name `xml:"s"` | ||
Bool optional.Optional[bool] `xml:"bool"` | ||
Byte optional.Optional[byte] `xml:"byte"` | ||
Float32 optional.Optional[float32] `xml:"float32"` | ||
|
@@ -585,7 +646,7 @@ func Example_xmlMarshalPresent() { | |
|
||
func Example_xmlUnmarshalEmpty() { | ||
s := struct { | ||
XMLName xml.Name `xml:"s"` | ||
XMLName xml.Name `xml:"s"` | ||
Bool optional.Optional[bool] `xml:"bool"` | ||
Byte optional.Optional[byte] `xml:"byte"` | ||
Float32 optional.Optional[float32] `xml:"float32"` | ||
|
@@ -646,7 +707,7 @@ func Example_xmlUnmarshalEmpty() { | |
|
||
func Example_xmlUnmarshalPresent() { | ||
s := struct { | ||
XMLName xml.Name `xml:"s"` | ||
XMLName xml.Name `xml:"s"` | ||
Bool optional.Optional[bool] `xml:"bool"` | ||
Byte optional.Optional[byte] `xml:"byte"` | ||
Float32 optional.Optional[float32] `xml:"float32"` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,11 @@ | ||
module 4d63.com/optional | ||
|
||
go 1.18 | ||
|
||
require github.com/stretchr/testify v1.7.2 | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.0 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -82,17 +82,28 @@ func (o Optional[T]) ElseZero() (value T) { | |
// representation of the zero value of the type wrapped if there is no value | ||
// wrapped by this optional. | ||
func (o Optional[T]) String() string { | ||
return fmt.Sprintf("%v", o.ElseZero()) | ||
if v, ok := o.Get(); ok { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment for the function needs updating as it says if no value is set that the zero value of the type is returned, which is no longer the case. |
||
return fmt.Sprintf("%v", v) | ||
} | ||
return "" | ||
} | ||
|
||
// MarshalJSON marshals the value being wrapped to JSON. If there is no vale | ||
// being wrapped, the zero value of its type is marshaled. | ||
func (o Optional[T]) MarshalJSON() (data []byte, err error) { | ||
return json.Marshal(o.ElseZero()) | ||
if v, ok := o.Get(); ok { | ||
return json.Marshal(v) | ||
} | ||
return []byte("null"), nil | ||
} | ||
|
||
// UnmarshalJSON unmarshals the JSON into a value wrapped by this optional. | ||
func (o *Optional[T]) UnmarshalJSON(data []byte) error { | ||
if string(data) == "null" { | ||
*o = Empty[T]() | ||
return nil | ||
} | ||
|
||
var v T | ||
err := json.Unmarshal(data, &v) | ||
if err != nil { | ||
|
@@ -105,7 +116,10 @@ func (o *Optional[T]) UnmarshalJSON(data []byte) error { | |
// MarshalXML marshals the value being wrapped to XML. If there is no vale | ||
// being wrapped, the zero value of its type is marshaled. | ||
func (o Optional[T]) MarshalXML(e *xml.Encoder, start xml.StartElement) error { | ||
return e.EncodeElement(o.ElseZero(), start) | ||
if v, ok := o.Get(); ok { | ||
return e.EncodeElement(v, start) | ||
} | ||
return e.EncodeElement("", start) | ||
} | ||
|
||
// UnmarshalXML unmarshals the XML into a value wrapped by this optional. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we keep this as an example? Examples already assert on the output.