Skip to content

Commit

Permalink
Unmarshal null should be have like json.Unmarshal
Browse files Browse the repository at this point in the history
There's a subtle behavior in json.Unmarshal that needs to be captured.
When unmashaling `null` it:
- basic type - leaves existing value
- pointer - overwrites with nil
- interface{} - overwrites with nil

Current behavour is incorrect because it returns an error, and the aim
should be to behave in the same way as json.Unmarshal
  • Loading branch information
blgm committed May 20, 2020
1 parent b3349b2 commit 59e4857
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 5 deletions.
25 changes: 20 additions & 5 deletions unmarhsal.go → unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,35 +104,50 @@ func unmarshalInfoLeaf(ctx context.Context, target reflect.Value, found bool, so
return unmarshalInfoLeaf(ctx, allocateIfNeeded(target), found, source)
}
case reflect.String:
if s, ok := source.(string); ok {
switch s := source.(type) {
case string:
target.SetString(s)
return nil
case nil:
return nil
}
case reflect.Bool:
if b, ok := source.(bool); ok {
switch b := source.(type) {
case bool:
target.SetBool(b)
return nil
case nil:
return nil
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if n, ok := source.(json.Number); ok {
switch n := source.(type) {
case json.Number:
if i, err := strconv.ParseInt(n.String(), 10, 64); err == nil {
target.SetInt(i)
return nil
}
case nil:
return nil
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if n, ok := source.(json.Number); ok {
switch n := source.(type) {
case json.Number:
if i, err := strconv.ParseUint(n.String(), 10, 64); err == nil {
target.SetUint(i)
return nil
}
case nil:
return nil
}
case reflect.Float32, reflect.Float64:
if n, ok := source.(json.Number); ok {
switch n := source.(type) {
case json.Number:
if f, err := strconv.ParseFloat(n.String(), 64); err == nil {
target.SetFloat(f)
return nil
}
case nil:
return nil
}
case reflect.Interface:
switch source {
Expand Down
37 changes: 37 additions & 0 deletions unmarshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,43 @@ var _ = Describe("Unmarshal", func() {
expectToFail(&s, `{"A":12}`, `cannot unmarshal "12" type "number" into field "A" (type "string")`)
expectToFail(&s, `{"N":13}`, `cannot unmarshal "13" type "number" into field "N" (type "jsonry_test.named")`)
})

When("unmarshalling null", func() {
It("leaves basic types untouched", func() {
s := struct {
S string
T int
U uint
V float64
W bool
}{
S: "foo",
T: -65,
U: 12,
V: 3.14,
W: true,
}
unmarshal(&s, `{"S": null, "T": null, "U": null, "V": null, "W": null}`)
Expect(s.S).To(Equal("foo"))
Expect(s.T).To(Equal(-65))
Expect(s.U).To(Equal(uint(12)))
Expect(s.V).To(Equal(3.14))
Expect(s.W).To(BeTrue())
})

It("overwrites a pointer as nil", func() {
v := "hello"
s := struct{ S *string }{S: &v}
unmarshal(&s, `{"S": null}`)
Expect(s.S).To(BeNil())
})

It("overwrites an interface{} as nil", func() {
s := struct{ S interface{} }{S: "hello"}
unmarshal(&s, `{"S": null}`)
Expect(s.S).To(BeNil())
})
})
})

Describe("recursive composition", func() {
Expand Down

0 comments on commit 59e4857

Please sign in to comment.