Skip to content

Commit 5ea8c65

Browse files
j-watersJames Watersmromaszewiczclaude
authored
Improve deepobject unmarshalling to support nullable.Nullable and encode.TextUnmarshaler (#45)
* Add deepobject tests for more complex types Specifically, types that there are special edge-cases for * Add deepobject support for test unmarshalers * Make deepobject support nullable values * Strip nullable support, fix TextUnmarshaler placement Remove nullable.Nullable deep object support (moved to separate repo). Move TextUnmarshaler check into case reflect.Struct after the legacy Date/time.Time handlers, so it catches types like uuid.UUID without intercepting time.Time date-only fallback parsing. Add early returns to the Date and time.Time blocks which previously fell through. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: James Waters <jcw@netcraft.com> Co-authored-by: Marcin Romaszewicz <marcinr@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 00e51fe commit 5ea8c65

File tree

2 files changed

+19
-1
lines changed

2 files changed

+19
-1
lines changed

deepobject.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package runtime
22

33
import (
44
"bytes"
5+
"encoding"
56
"encoding/json"
67
"errors"
78
"fmt"
@@ -55,8 +56,16 @@ func marshalDeepObject(in interface{}, path []string) ([]string, error) {
5556
// into a deepObject style set of subscripts. [a, b, c] turns into
5657
// [a][b][c]
5758
prefix := "[" + strings.Join(path, "][") + "]"
59+
60+
var value string
61+
if t == nil {
62+
value = "null"
63+
} else {
64+
value = fmt.Sprintf("%v", t)
65+
}
66+
5867
result = []string{
59-
prefix + fmt.Sprintf("=%v", t),
68+
prefix + fmt.Sprintf("=%s", value),
6069
}
6170
}
6271
return result, nil
@@ -268,6 +277,7 @@ func assignPathValues(dst interface{}, pathValues fieldOrValue) error {
268277
dst = reflect.Indirect(aPtr)
269278
}
270279
dst.Set(reflect.ValueOf(date))
280+
return nil
271281
}
272282
if it.ConvertibleTo(reflect.TypeOf(time.Time{})) {
273283
var tm time.Time
@@ -288,6 +298,13 @@ func assignPathValues(dst interface{}, pathValues fieldOrValue) error {
288298
dst = reflect.Indirect(aPtr)
289299
}
290300
dst.Set(reflect.ValueOf(tm))
301+
return nil
302+
}
303+
// For other struct types that implement TextUnmarshaler (e.g. uuid.UUID),
304+
// use that for binding. This comes after the legacy Date/time.Time checks
305+
// above which have special fallback format handling.
306+
if tu, ok := v.Interface().(encoding.TextUnmarshaler); ok {
307+
return tu.UnmarshalText([]byte(pathValues.value))
291308
}
292309
fieldMap, err := fieldIndicesByJSONTag(iv.Interface())
293310
if err != nil {

deepobject_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ func TestDeepObject(t *testing.T) {
6363
om := map[string]int{
6464
"additional": 1,
6565
}
66+
6667
d := MockBinder{Time: time.Date(2020, 2, 1, 0, 0, 0, 0, time.UTC)}
6768

6869
two := 2

0 commit comments

Comments
 (0)