Ejemplo n.º 1
0
// 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)
}
Ejemplo n.º 2
0
// 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)
	}
}
Ejemplo n.º 3
0
// 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)
}