summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyle Erf <erf@mongodb.com>2015-08-04 15:54:54 -0400
committerKyle Erf <erf@mongodb.com>2015-08-18 15:25:22 -0400
commit61bfea23b0bc95c0859d893f80b3b48ed82567f4 (patch)
treeeb8bfb815055d36be9db7f2e72acce05aeb1b586
parent720440f7d66a49c89fd7adda5ce7c9560272359f (diff)
downloadmongo-61bfea23b0bc95c0859d893f80b3b48ed82567f4.tar.gz
TOOLS-767 fix decoding 'null' JSON into bson.D
-rw-r--r--common/json/decode.go49
-rw-r--r--common/json/decode_d_test.go33
2 files changed, 42 insertions, 40 deletions
diff --git a/common/json/decode.go b/common/json/decode.go
index 941c9df2265..850b17a5059 100644
--- a/common/json/decode.go
+++ b/common/json/decode.go
@@ -599,14 +599,18 @@ func (d *decodeState) object(v reflect.Value) {
if v.IsNil() {
v.Set(reflect.MakeMap(t))
}
+
+ case reflect.Struct:
+ // do nothing
+
case reflect.Slice:
// this is only a valid case if the output type is a bson.D
t := v.Type()
- if t != orderedBSONType {
- d.saveError(&UnmarshalTypeError{"object", v.Type()})
- break
+ if t == orderedBSONType {
+ v.Set(reflect.ValueOf(d.bsonDInterface()))
+ return
}
- case reflect.Struct:
+ fallthrough // can't unmarshal into a regular slice, goto "default" error case
default:
d.saveError(&UnmarshalTypeError{"object", v.Type()})
@@ -617,8 +621,6 @@ func (d *decodeState) object(v reflect.Value) {
var mapElem reflect.Value
- i := 0
- bsonDMode := false
for {
// Read opening " of string key or closing }.
op := d.scanWhile(scanSkipSpace)
@@ -651,14 +653,6 @@ func (d *decodeState) object(v reflect.Value) {
mapElem.Set(reflect.Zero(elemType))
}
subv = mapElem
- } else if v.Kind() == reflect.Slice {
- bsonDMode = true
- elemType := interfaceType.Elem()
- if !mapElem.IsValid() {
- subv = reflect.New(elemType).Elem()
- } else {
- mapElem.Set(reflect.Zero(elemType))
- }
} else {
var f *field
fields := cachedTypeFields(v.Type())
@@ -695,18 +689,12 @@ func (d *decodeState) object(v reflect.Value) {
d.error(errPhase)
}
// Read value.
- var docElemValue interface{}
if destring {
d.value(reflect.ValueOf(&d.tempstr))
d.literalStore([]byte(d.tempstr), subv, true)
d.tempstr = "" // Zero scratch space for successive values.
} else {
- if bsonDMode {
- docElemValue = d.valueInterface()
- subv.Set(reflect.ValueOf(docElemValue))
- } else {
- d.value(subv)
- }
+ d.value(subv)
}
// Write value back to map;
@@ -714,24 +702,6 @@ func (d *decodeState) object(v reflect.Value) {
if v.Kind() == reflect.Map {
kv := reflect.ValueOf(key).Convert(v.Type().Key())
v.SetMapIndex(kv, subv)
- } else if v.Kind() == reflect.Slice {
- kv := reflect.ValueOf(key).Convert(stringType)
- newDocElem := &bson.DocElem{kv.String(), subv.Interface()}
- // Slice construction/resizing code is from decodeState.array()
- if i >= v.Cap() {
- newcap := v.Cap() + v.Cap()/2
- if newcap < 4 {
- newcap = 4
- }
- newv := reflect.MakeSlice(v.Type(), v.Len(), newcap)
- reflect.Copy(newv, v)
- v.Set(newv)
- }
- if i >= v.Len() {
- v.SetLen(i + 1)
- }
- v3 := v.Index(i)
- v3.Set(reflect.ValueOf(newDocElem).Elem())
}
// Next token must be , or }.
@@ -742,7 +712,6 @@ func (d *decodeState) object(v reflect.Value) {
if op != scanObjectValue {
d.error(errPhase)
}
- i++
}
}
diff --git a/common/json/decode_d_test.go b/common/json/decode_d_test.go
index 63e4bd89d65..76223ddecd1 100644
--- a/common/json/decode_d_test.go
+++ b/common/json/decode_d_test.go
@@ -23,6 +23,24 @@ func TestDecodeBsonD(t *testing.T) {
})
+ Convey("A nested bson.D should be parsed", func() {
+ data := `{"a": 17, "b":{"foo":"bar", "baz":"boo"}, c:"wow" }`
+ out := struct {
+ A int `json:"a"`
+ B bson.D `json:"b"`
+ C string `json:"c"`
+ }{}
+ err := Unmarshal([]byte(data), &out)
+ So(err, ShouldBeNil)
+ So(out.A, ShouldEqual, 17)
+ So(out.C, ShouldEqual, "wow")
+ So(len(out.B), ShouldEqual, 2)
+ So(out.B[0].Name, ShouldEqual, "foo")
+ So(out.B[0].Value, ShouldEqual, "bar")
+ So(out.B[1].Name, ShouldEqual, "baz")
+ So(out.B[1].Value, ShouldEqual, "boo")
+ })
+
Convey("Objects nested within DocElems should still be parsed", func() {
data := `{"a":["x", "y","z"], "b":{"foo":"bar", "baz":"boo"}}`
out := bson.D{}
@@ -34,6 +52,21 @@ func TestDecodeBsonD(t *testing.T) {
So(out[0].Value, ShouldResemble, []interface{}{"x", "y", "z"})
So(out[1].Value, ShouldResemble, map[string]interface{}{"foo": "bar", "baz": "boo"})
})
+
+ Convey("null should be a valid value", func() {
+ data := `{"a":true, "b":null, "c": 5}`
+ out := bson.D{}
+ err := Unmarshal([]byte(data), &out)
+ So(err, ShouldBeNil)
+ So(len(out), ShouldEqual, 3)
+ So(out[0].Name, ShouldEqual, "a")
+ So(out[0].Value, ShouldEqual, true)
+ So(out[1].Name, ShouldEqual, "b")
+ So(out[1].Value, ShouldBeNil)
+ So(out[2].Name, ShouldEqual, "c")
+ So(out[2].Value, ShouldEqual, 5)
+ })
+
})
Convey("Unmarshalling to a non-bson.D slice types should fail", t, func() {
data := `{"a":["x", "y","z"], "b":{"foo":"bar", "baz":"boo"}}`