// Returns a Date literal from the underlying byte data. func (d *decodeState) getDate() interface{} { op := d.scanWhile(scanSkipSpace) if op != scanBeginCtor { d.error(fmt.Errorf("expected beginning of constructor")) } // Prevent d.convertNumber() from parsing the argument as a float64. useNumber := d.useNumber d.useNumber = true args := d.ctorInterface() if err := ctorNumArgsMismatch("Date", 1, len(args)); err != nil { d.error(err) } arg0num, isNumber := args[0].(Number) if !isNumber { // validate the date format of the string _, err := util.FormatDate(args[0].(string)) if err != nil { d.error(fmt.Errorf("unexpected ISODate format")) } d.useNumber = useNumber return ISODate(args[0].(string)) } arg0, err := arg0num.Int64() if err != nil { d.error(fmt.Errorf("expected int64 for first argument of Date constructor")) } d.useNumber = useNumber return Date(arg0) }
// ConvertJSONValueToBSON walks through a document or an array and // replaces any extended JSON value with its corresponding BSON type. func ConvertJSONValueToBSON(x interface{}) (interface{}, error) { switch v := x.(type) { case nil: return nil, nil case bool: return v, nil case map[string]interface{}: // document for key, jsonValue := range v { bsonValue, err := ParseJSONValue(jsonValue) if err != nil { return nil, err } v[key] = bsonValue } return v, nil case bson.D: for i := range v { var err error v[i].Value, err = ParseJSONValue(v[i].Value) if err != nil { return nil, err } } return v, nil case []interface{}: // array for i, jsonValue := range v { bsonValue, err := ParseJSONValue(jsonValue) if err != nil { return nil, err } v[i] = bsonValue } return v, nil case string, float64, int32, int64: return v, nil // require no conversion case json.ObjectId: // ObjectId s := string(v) if !bson.IsObjectIdHex(s) { return nil, errors.New("expected ObjectId to contain 24 hexadecimal characters") } return bson.ObjectIdHex(s), nil case json.Decimal128: return v.Decimal128, nil case json.Date: // Date n := int64(v) return time.Unix(n/1e3, n%1e3*1e6), nil case json.ISODate: // ISODate n := string(v) return util.FormatDate(n) case json.NumberLong: // NumberLong return int64(v), nil case json.NumberInt: // NumberInt return int32(v), nil case json.NumberFloat: // NumberFloat return float64(v), nil case json.BinData: // BinData data, err := base64.StdEncoding.DecodeString(v.Base64) if err != nil { return nil, err } return bson.Binary{v.Type, data}, nil case json.DBRef: // DBRef var err error v.Id, err = ParseJSONValue(v.Id) if err != nil { return nil, err } return mgo.DBRef{v.Collection, v.Id, v.Database}, nil case json.DBPointer: // DBPointer, for backwards compatibility return bson.DBPointer{v.Namespace, v.Id}, nil case json.RegExp: // RegExp return bson.RegEx{v.Pattern, v.Options}, nil case json.Timestamp: // Timestamp ts := (int64(v.Seconds) << 32) | int64(v.Increment) return bson.MongoTimestamp(ts), nil case json.JavaScript: // Javascript return bson.JavaScript{v.Code, v.Scope}, nil case json.MinKey: // MinKey return bson.MinKey, nil case json.MaxKey: // MaxKey return bson.MaxKey, nil case json.Undefined: // undefined return bson.Undefined, nil default: return nil, fmt.Errorf("conversion of JSON value '%v' of type '%T' not supported", v, v) } }
// ParseSpecialKeys takes a JSON document and inspects it for any extended JSON // type (e.g $numberLong) and replaces any such values with the corresponding // BSON type. func ParseSpecialKeys(doc map[string]interface{}) (interface{}, error) { switch len(doc) { case 1: // document has a single field if jsonValue, ok := doc["$date"]; ok { switch v := jsonValue.(type) { case string: return util.FormatDate(v) case map[string]interface{}: if jsonValue, ok := v["$numberLong"]; ok { n, err := parseNumberLongField(jsonValue) if err != nil { return nil, err } return time.Unix(n/1e3, n%1e3*1e6), err } return nil, errors.New("expected $numberLong field in $date") case json.Number: n, err := v.Int64() return time.Unix(n/1e3, n%1e3*1e6), err case float64: n := int64(v) return time.Unix(n/1e3, n%1e3*1e6), nil case int32: n := int64(v) return time.Unix(n/1e3, n%1e3*1e6), nil case int64: return time.Unix(v/1e3, v%1e3*1e6), nil case json.ISODate: return v, nil default: return nil, errors.New("invalid type for $date field") } } if jsonValue, ok := doc["$code"]; ok { switch v := jsonValue.(type) { case string: return bson.JavaScript{Code: v}, nil default: return nil, errors.New("expected $code field to have string value") } } if jsonValue, ok := doc["$oid"]; ok { switch v := jsonValue.(type) { case string: if !bson.IsObjectIdHex(v) { return nil, errors.New("expected $oid field to contain 24 hexadecimal character") } return bson.ObjectIdHex(v), nil default: return nil, errors.New("expected $oid field to have string value") } } if jsonValue, ok := doc["$numberLong"]; ok { return parseNumberLongField(jsonValue) } if jsonValue, ok := doc["$numberInt"]; ok { switch v := jsonValue.(type) { case string: // all of decimal, hex, and octal are supported here n, err := strconv.ParseInt(v, 0, 32) return int32(n), err default: return nil, errors.New("expected $numberInt field to have string value") } } if jsonValue, ok := doc["$timestamp"]; ok { ts := json.Timestamp{} tsDoc, ok := jsonValue.(map[string]interface{}) if !ok { return nil, errors.New("expected $timestamp key to have internal document") } if seconds, ok := tsDoc["t"]; ok { if asUint32, err := util.ToUInt32(seconds); err == nil { ts.Seconds = asUint32 } else { return nil, errors.New("expected $timestamp 't' field to be a numeric type") } } else { return nil, errors.New("expected $timestamp to have 't' field") } if inc, ok := tsDoc["i"]; ok { if asUint32, err := util.ToUInt32(inc); err == nil { ts.Increment = asUint32 } else { return nil, errors.New("expected $timestamp 'i' field to be a numeric type") } } else { return nil, errors.New("expected $timestamp to have 'i' field") } // see BSON spec for details on the bit fiddling here return bson.MongoTimestamp(int64(ts.Seconds)<<32 | int64(ts.Increment)), nil } if _, ok := doc["$undefined"]; ok { return bson.Undefined, nil } if _, ok := doc["$maxKey"]; ok { return bson.MaxKey, nil } if _, ok := doc["$minKey"]; ok { return bson.MinKey, nil } case 2: // document has two fields if jsonValue, ok := doc["$code"]; ok { code := bson.JavaScript{} switch v := jsonValue.(type) { case string: code.Code = v default: return nil, errors.New("expected $code field to have string value") } if jsonValue, ok = doc["$scope"]; ok { switch v2 := jsonValue.(type) { case map[string]interface{}: x, err := ParseSpecialKeys(v2) if err != nil { return nil, err } code.Scope = x return code, nil default: return nil, errors.New("expected $scope field to contain map") } } else { return nil, errors.New("expected $scope field with $code field") } } if jsonValue, ok := doc["$regex"]; ok { regex := bson.RegEx{} switch pattern := jsonValue.(type) { case string: regex.Pattern = pattern default: return nil, errors.New("expected $regex field to have string value") } if jsonValue, ok = doc["$options"]; !ok { return nil, errors.New("expected $options field with $regex field") } switch options := jsonValue.(type) { case string: regex.Options = options default: return nil, errors.New("expected $options field to have string value") } // Validate regular expression options for i := range regex.Options { switch o := regex.Options[i]; o { default: return nil, fmt.Errorf("invalid regular expression option '%v'", o) case 'g', 'i', 'm', 's': // allowed } } return regex, nil } if jsonValue, ok := doc["$binary"]; ok { binary := bson.Binary{} switch data := jsonValue.(type) { case string: bytes, err := base64.StdEncoding.DecodeString(data) if err != nil { return nil, err } binary.Data = bytes default: return nil, errors.New("expected $binary field to have string value") } if jsonValue, ok = doc["$type"]; !ok { return nil, errors.New("expected $type field with $binary field") } switch typ := jsonValue.(type) { case string: kind, err := hex.DecodeString(typ) if err != nil { return nil, err } else if len(kind) != 1 { return nil, errors.New("expected single byte (as hexadecimal string) for $type field") } binary.Kind = kind[0] default: return nil, errors.New("expected $type field to have string value") } return binary, nil } if jsonValue, ok := doc["$ref"]; ok { dbRef := mgo.DBRef{} switch data := jsonValue.(type) { case string: dbRef.Collection = data default: return nil, errors.New("expected string for $ref field") } if jsonValue, ok = doc["$id"]; ok { switch v2 := jsonValue.(type) { case map[string]interface{}: x, err := ParseSpecialKeys(v2) if err != nil { return nil, fmt.Errorf("error parsing $id field: %v", err) } dbRef.Id = x default: dbRef.Id = v2 } return dbRef, nil } } case 3: if jsonValue, ok := doc["$ref"]; ok { dbRef := mgo.DBRef{} switch data := jsonValue.(type) { case string: dbRef.Collection = data default: return nil, errors.New("expected string for $ref field") } if jsonValue, ok = doc["$id"]; ok { switch v2 := jsonValue.(type) { case map[string]interface{}: x, err := ParseSpecialKeys(v2) if err != nil { return nil, fmt.Errorf("error parsing $id field: %v", err) } dbRef.Id = x default: dbRef.Id = v2 } if dbValue, ok := doc["$db"]; ok { switch v3 := dbValue.(type) { case string: dbRef.Database = v3 default: return nil, errors.New("expected string for $db field") } return dbRef, nil } } } } // Did not match any special ('$') keys, so convert all sub-values. return ConvertJSONValueToBSON(doc) }