1// Copyright (C) MongoDB, Inc. 2014-present. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may 4// not use this file except in compliance with the License. You may obtain 5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 7package bsonutil 8 9import ( 10 "encoding/base64" 11 "fmt" 12 "time" 13 14 "github.com/mongodb/mongo-tools-common/json" 15 "github.com/mongodb/mongo-tools-common/util" 16 "go.mongodb.org/mongo-driver/bson" 17 "go.mongodb.org/mongo-driver/bson/primitive" 18) 19 20// ConvertLegacyExtJSONValueToBSON walks through a document or an array and 21// replaces any extended JSON value with its corresponding BSON type. 22func ConvertLegacyExtJSONValueToBSON(x interface{}) (interface{}, error) { 23 switch v := x.(type) { 24 case nil: 25 return nil, nil 26 case bool: 27 return v, nil 28 case map[string]interface{}: // document 29 for key, jsonValue := range v { 30 bsonValue, err := ParseLegacyExtJSONValue(jsonValue) 31 if err != nil { 32 return nil, err 33 } 34 v[key] = bsonValue 35 } 36 return v, nil 37 case bson.D: 38 for i := range v { 39 var err error 40 v[i].Value, err = ParseLegacyExtJSONValue(v[i].Value) 41 if err != nil { 42 return nil, err 43 } 44 } 45 return v, nil 46 47 case []interface{}: // array 48 for i, jsonValue := range v { 49 bsonValue, err := ParseLegacyExtJSONValue(jsonValue) 50 if err != nil { 51 return nil, err 52 } 53 v[i] = bsonValue 54 } 55 return v, nil 56 57 case string, float64, int32, int64: 58 return v, nil // require no conversion 59 60 case json.ObjectId: // ObjectId 61 s := string(v) 62 return primitive.ObjectIDFromHex(s) 63 64 case json.Decimal128: 65 return v.Decimal128, nil 66 67 case json.Date: // Date 68 n := int64(v) 69 return time.Unix(n/1e3, n%1e3*1e6), nil 70 71 case json.ISODate: // ISODate 72 n := string(v) 73 return util.FormatDate(n) 74 75 case json.NumberLong: // NumberLong 76 return int64(v), nil 77 78 case json.NumberInt: // NumberInt 79 return int32(v), nil 80 81 case json.NumberFloat: // NumberFloat 82 return float64(v), nil 83 case json.BinData: // BinData 84 data, err := base64.StdEncoding.DecodeString(v.Base64) 85 if err != nil { 86 return nil, err 87 } 88 return primitive.Binary{v.Type, data}, nil 89 90 case json.DBPointer: // DBPointer, for backwards compatibility 91 return primitive.DBPointer{v.Namespace, v.Id}, nil 92 93 case json.RegExp: // RegExp 94 return primitive.Regex{v.Pattern, v.Options}, nil 95 96 case json.Timestamp: // Timestamp 97 return primitive.Timestamp{T: v.Seconds, I: v.Increment}, nil 98 99 case json.JavaScript: // Javascript 100 if v.Scope != nil { 101 return primitive.CodeWithScope{Code: primitive.JavaScript(v.Code), Scope: v.Scope}, nil 102 } 103 return primitive.JavaScript(v.Code), nil 104 105 case json.MinKey: // MinKey 106 return primitive.MinKey{}, nil 107 108 case json.MaxKey: // MaxKey 109 return primitive.MaxKey{}, nil 110 111 case json.Undefined: // undefined 112 return primitive.Undefined{}, nil 113 114 default: 115 return nil, fmt.Errorf("conversion of JSON value '%v' of type '%T' not supported", v, v) 116 } 117} 118 119func convertKeys(v bson.M) (bson.M, error) { 120 for key, value := range v { 121 jsonValue, err := ConvertBSONValueToLegacyExtJSON(value) 122 if err != nil { 123 return nil, err 124 } 125 v[key] = jsonValue 126 } 127 return v, nil 128} 129 130func getConvertedKeys(v bson.M) (bson.M, error) { 131 out := bson.M{} 132 for key, value := range v { 133 jsonValue, err := GetBSONValueAsLegacyExtJSON(value) 134 if err != nil { 135 return nil, err 136 } 137 out[key] = jsonValue 138 } 139 return out, nil 140} 141 142func convertArray(v bson.A) ([]interface{}, error) { 143 for i, value := range v { 144 jsonValue, err := ConvertBSONValueToLegacyExtJSON(value) 145 if err != nil { 146 return nil, err 147 } 148 v[i] = jsonValue 149 } 150 return []interface{}(v), nil 151} 152 153// ConvertBSONValueToLegacyExtJSON walks through a document or an array and 154// converts any BSON value to its corresponding extended JSON type. 155// It returns the converted JSON document and any error encountered. 156func ConvertBSONValueToLegacyExtJSON(x interface{}) (interface{}, error) { 157 switch v := x.(type) { 158 case nil: 159 return nil, nil 160 case bool: 161 return v, nil 162 163 case *bson.M: // document 164 doc, err := convertKeys(*v) 165 if err != nil { 166 return nil, err 167 } 168 return doc, err 169 case bson.M: // document 170 return convertKeys(v) 171 case map[string]interface{}: 172 return convertKeys(v) 173 case bson.D: 174 for i, value := range v { 175 jsonValue, err := ConvertBSONValueToLegacyExtJSON(value.Value) 176 if err != nil { 177 return nil, err 178 } 179 v[i].Value = jsonValue 180 } 181 return MarshalD(v), nil 182 case MarshalD: 183 return v, nil 184 case bson.A: // array 185 return convertArray(v) 186 case []interface{}: // array 187 return convertArray(v) 188 case string: 189 return v, nil // require no conversion 190 191 case int: 192 return json.NumberInt(v), nil 193 194 case primitive.ObjectID: // ObjectId 195 return json.ObjectId(v.Hex()), nil 196 197 case primitive.Decimal128: 198 return json.Decimal128{v}, nil 199 200 case primitive.DateTime: // Date 201 return json.Date(v), nil 202 203 case time.Time: // Date 204 return json.Date(v.Unix()*1000 + int64(v.Nanosecond()/1e6)), nil 205 206 case int64: // NumberLong 207 return json.NumberLong(v), nil 208 209 case int32: // NumberInt 210 return json.NumberInt(v), nil 211 212 case float64: 213 return json.NumberFloat(v), nil 214 215 case float32: 216 return json.NumberFloat(float64(v)), nil 217 218 case []byte: // BinData (with generic type) 219 data := base64.StdEncoding.EncodeToString(v) 220 return json.BinData{0x00, data}, nil 221 222 case primitive.Binary: // BinData 223 data := base64.StdEncoding.EncodeToString(v.Data) 224 return json.BinData{v.Subtype, data}, nil 225 226 case primitive.DBPointer: // DBPointer 227 return json.DBPointer{v.DB, v.Pointer}, nil 228 229 case primitive.Regex: // RegExp 230 return json.RegExp{v.Pattern, v.Options}, nil 231 232 case primitive.Timestamp: // Timestamp 233 return json.Timestamp{ 234 Seconds: v.T, 235 Increment: v.I, 236 }, nil 237 238 case primitive.JavaScript: // JavaScript Code 239 return json.JavaScript{Code: string(v), Scope: nil}, nil 240 241 case primitive.CodeWithScope: // JavaScript Code w/ Scope 242 var scope interface{} 243 var err error 244 if v.Scope != nil { 245 scope, err = ConvertBSONValueToLegacyExtJSON(v.Scope) 246 if err != nil { 247 return nil, err 248 } 249 } 250 return json.JavaScript{string(v.Code), scope}, nil 251 252 case primitive.MaxKey: // MaxKey 253 return json.MaxKey{}, nil 254 255 case primitive.MinKey: // MinKey 256 return json.MinKey{}, nil 257 258 case primitive.Undefined: // undefined 259 return json.Undefined{}, nil 260 261 case primitive.Null: // Null 262 return nil, nil 263 } 264 265 return nil, fmt.Errorf("conversion of BSON value '%v' of type '%T' not supported", x, x) 266} 267 268// GetBSONValueAsLegacyExtJSON is equivalent to ConvertBSONValueToLegacyExtJSON, but does not mutate its argument. 269func GetBSONValueAsLegacyExtJSON(x interface{}) (interface{}, error) { 270 switch v := x.(type) { 271 case nil: 272 return nil, nil 273 case bool: 274 return v, nil 275 276 case *bson.M: // document 277 doc, err := getConvertedKeys(*v) 278 if err != nil { 279 return nil, err 280 } 281 return doc, err 282 case bson.M: // document 283 return getConvertedKeys(v) 284 case map[string]interface{}: 285 return getConvertedKeys(v) 286 case bson.D: 287 out := bson.D{} 288 for _, value := range v { 289 jsonValue, err := GetBSONValueAsLegacyExtJSON(value.Value) 290 if err != nil { 291 return nil, err 292 } 293 out = append(out, bson.E{ 294 Key: value.Key, 295 Value: jsonValue, 296 }) 297 } 298 return MarshalD(out), nil 299 case MarshalD: 300 out, err := GetBSONValueAsLegacyExtJSON(bson.D(v)) 301 if err != nil { 302 return nil, err 303 } 304 return MarshalD(out.(bson.D)), nil 305 case []interface{}: // array 306 out := []interface{}{} 307 for _, value := range v { 308 jsonValue, err := GetBSONValueAsLegacyExtJSON(value) 309 if err != nil { 310 return nil, err 311 } 312 out = append(out, jsonValue) 313 } 314 return out, nil 315 316 case string: 317 return v, nil // require no conversion 318 319 case int: 320 return json.NumberInt(v), nil 321 322 case primitive.ObjectID: // ObjectId 323 return json.ObjectId(v.Hex()), nil 324 325 case primitive.Decimal128: 326 return json.Decimal128{v}, nil 327 328 case primitive.DateTime: // Date 329 return json.Date(v), nil 330 331 case time.Time: // Date 332 return json.Date(v.Unix()*1000 + int64(v.Nanosecond()/1e6)), nil 333 334 case int64: // NumberLong 335 return json.NumberLong(v), nil 336 337 case int32: // NumberInt 338 return json.NumberInt(v), nil 339 340 case float64: 341 return json.NumberFloat(v), nil 342 343 case float32: 344 return json.NumberFloat(float64(v)), nil 345 346 case []byte: // BinData (with generic type) 347 data := base64.StdEncoding.EncodeToString(v) 348 return json.BinData{0x00, data}, nil 349 350 case primitive.Binary: // BinData 351 data := base64.StdEncoding.EncodeToString(v.Data) 352 return json.BinData{Type: v.Subtype, Base64: data}, nil 353 354 case primitive.DBPointer: // DBPointer 355 return json.DBPointer{v.DB, v.Pointer}, nil 356 357 case primitive.Regex: // RegExp 358 return json.RegExp{v.Pattern, v.Options}, nil 359 360 case primitive.Timestamp: // Timestamp 361 return json.Timestamp{ 362 Seconds: v.T, 363 Increment: v.I, 364 }, nil 365 366 case primitive.JavaScript: // JavaScript Code 367 return json.JavaScript{Code: string(v), Scope: nil}, nil 368 369 case primitive.CodeWithScope: // JavaScript Code w/ Scope 370 var scope interface{} 371 var err error 372 if v.Scope != nil { 373 scope, err = GetBSONValueAsLegacyExtJSON(v.Scope) 374 if err != nil { 375 return nil, err 376 } 377 } 378 return json.JavaScript{string(v.Code), scope}, nil 379 380 case primitive.MaxKey: // MaxKey 381 return json.MaxKey{}, nil 382 383 case primitive.MinKey: // MinKey 384 return json.MinKey{}, nil 385 386 case primitive.Undefined: // undefined 387 return json.Undefined{}, nil 388 389 case primitive.Null: // Null 390 return nil, nil 391 } 392 393 return nil, fmt.Errorf("conversion of BSON value '%v' of type '%T' not supported", x, x) 394} 395