Skip to content

Commit e546c4f

Browse files
author
James Waters
committed
Make deepobject support nullable values
1 parent daeb3b9 commit e546c4f

File tree

2 files changed

+63
-1
lines changed

2 files changed

+63
-1
lines changed

deepobject.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ import (
1515
"github.com/oapi-codegen/runtime/types"
1616
)
1717

18+
type nullableLike interface {
19+
SetNull()
20+
UnmarshalJSON(data []byte) error
21+
}
22+
1823
func marshalDeepObject(in interface{}, path []string) ([]string, error) {
1924
var result []string
2025

@@ -55,8 +60,16 @@ func marshalDeepObject(in interface{}, path []string) ([]string, error) {
5560
// into a deepObject style set of subscripts. [a, b, c] turns into
5661
// [a][b][c]
5762
prefix := "[" + strings.Join(path, "][") + "]"
63+
64+
var value string
65+
if t == nil {
66+
value = "null"
67+
} else {
68+
value = fmt.Sprintf("%v", t)
69+
}
70+
5871
result = []string{
59-
prefix + fmt.Sprintf("=%v", t),
72+
prefix + fmt.Sprintf("=%s", value),
6073
}
6174
}
6275
return result, nil
@@ -214,6 +227,38 @@ func assignPathValues(dst interface{}, pathValues fieldOrValue) error {
214227

215228
switch it.Kind() {
216229
case reflect.Map:
230+
// If the value looks like nullable.Nullable[T], we need to handle it properly.
231+
if dst, ok := dst.(nullableLike); ok {
232+
if pathValues.value == "null" {
233+
dst.SetNull()
234+
235+
return nil
236+
}
237+
238+
// We create a new empty value, who's type is the same as the
239+
// 'T' in nullable.Nullable[T]. Because of how nullable.Nullable is
240+
// implemented, we can do that by getting the type's element type.
241+
data := reflect.New(it.Elem()).Interface()
242+
243+
// We now try to assign the path values to the new type.
244+
if err := assignPathValues(data, pathValues); err != nil {
245+
return err
246+
}
247+
248+
// We'll marshal the data so that we can unmarshal it into
249+
// the original nullable.Nullable value.
250+
dataBytes, err := json.Marshal(data)
251+
if err != nil {
252+
return err
253+
}
254+
255+
if err := dst.UnmarshalJSON(dataBytes); err != nil {
256+
return err
257+
}
258+
259+
return nil
260+
}
261+
217262
dstMap := reflect.MakeMap(iv.Type())
218263
for key, value := range pathValues.fields {
219264
dstKey := reflect.ValueOf(key)

deepobject_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
"github.com/google/uuid"
10+
"github.com/oapi-codegen/nullable"
1011
"github.com/oapi-codegen/runtime/types"
1112
"github.com/stretchr/testify/assert"
1213
"github.com/stretchr/testify/require"
@@ -42,6 +43,13 @@ type AllFields struct {
4243
Oti *time.Time `json:"oti,omitempty"`
4344
U types.UUID `json:"u"`
4445
Ou *types.UUID `json:"ou,omitempty"`
46+
47+
// Nullable
48+
NiSet nullable.Nullable[int] `json:"ni_set,omitempty"`
49+
NiNull nullable.Nullable[int] `json:"ni_null,omitempty"`
50+
NiUnset nullable.Nullable[int] `json:"ni_unset,omitempty"`
51+
No nullable.Nullable[InnerObject] `json:"no,omitempty"`
52+
Nu nullable.Nullable[uuid.UUID] `json:"nu,omitempty"`
4553
}
4654

4755
func TestDeepObject(t *testing.T) {
@@ -89,6 +97,15 @@ func TestDeepObject(t *testing.T) {
8997
Oti: &ti,
9098
U: u,
9199
Ou: &u,
100+
101+
// Nullable
102+
NiSet: nullable.NewNullableWithValue(5),
103+
NiNull: nullable.NewNullNullable[int](),
104+
No: nullable.NewNullableWithValue(InnerObject{
105+
Name: "John Smith",
106+
ID: 456,
107+
}),
108+
Nu: nullable.NewNullableWithValue(uuid.New()),
92109
}
93110

94111
marshaled, err := MarshalDeepObject(srcObj, "p")

0 commit comments

Comments
 (0)