草庐IT

swift - swift 的BSON?

coder 2023-09-15 原文

此时我的问题很开放,但我很好奇是否有人在 Swift 中为 BSON 实现了类似 SwiftyJSON 的东西?

我发现 Perfect project 有一些东西,但它似乎只是一个现有 C 库的包装器(在 iOS 方面对我没有任何好处)。我只是玩弄了 SwiftyJSON 的移植/变形,到目前为止,它的内部结构略高于我的学习曲线,而且它似乎只是平台的 JSONSerialization 的包装器。

谁也一样

A) 知道我的谷歌搜索还没有发现的事情,或者

B) 或者可以帮助我在正确的方向上插入我如何总体架构这样的东西? (不是试图让别人为我工作)

旁白:为了避免“你为什么不直接使用 json”的问题...这是因为我在另一边做了相当多的 MongoDB,而且我使用了很多字符串和日期,必须在 JSON 中模糊表示

最佳答案

为了结束......我最终写了自己的。它不是针对所有 BSON 编码的完整解决方案,只是我正在使用的编码。弄清楚如何使用 Swift 枚举来做到这一点很有趣。

import Foundation

extension GeneratorType {
    mutating func next(n: Int) -> [Element] {
        var result: [Element] = []
        for _ in 1...n {
            if let next = self.next() {
                result.append(next)
            } else {
                break
            }
        }
        return result
    }
}

extension GeneratorType where Element:Comparable {
    mutating func upTo(match:Element) -> [Element]? {
        var result: [Element] = []
        while let next = self.next() {
            if next == match {
                return result
            }
            else {
                result.append(next)
            }
        }
        return nil
    }
}

extension String {
    init?<S : SequenceType, C : UnicodeCodecType where S.Generator.Element == C.CodeUnit>
        (codeUnits : S, inout codec : C) {
            var str = ""
            var generator = codeUnits.generate()
            var done = false
            while !done {
                let r = codec.decode(&generator)
                switch (r) {
                case .EmptyInput:
                    done = true
                case let .Result(val):
                    str.append(Character(val))
                case .Error:
                    return nil
                }
            }
            self = str
    }
}

enum BSON {
    static func toByteArray<T>(value: T) -> [UInt8] {
        var io = value
        return withUnsafePointer(&io) {
            Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>($0), count: sizeof(T)))
        }
    }

    static func fromByteArray<T>(value: [UInt8], _: T.Type) -> T {
        return value.withUnsafeBufferPointer {
            return UnsafePointer<T>($0.baseAddress).memory
        }
    }

    struct Field {
        var name:String
        var element:BSON
    }

    case double(Double)
    case string(String)
    case document([Field])
    case array([BSON])
    case binary([UInt8])
//  case objectid((UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8))
    case boolean(Bool)
    case datetime(NSDate)
    case null
    case int32(Int32)
    case int64(Int64)

    init() {
        self = .document([])
    }

    var bsonType:UInt8 {
        switch self {
        case .double: return 0x01
        case .string: return 0x02
        case .document: return 0x03
        case .array: return 0x04
        case .binary: return 0x05
//      case .objectid: return 0x07
        case .boolean: return 0x08
        case .datetime: return 0x09
        case .null: return 0x0A
        case .int32: return 0x10
        case .int64: return 0x12
        }
    }

    subscript(key:String) -> BSON {
        get {
            switch self {
            case .document (let fields):
                for field in fields {
                    if field.name == key {
                        return field.element
                    }
                }
                return BSON.null
            default:
                return BSON.null
            }
        }
        set(newValue) {
            var newFields:[Field] = []
            switch self {
            case .document (let fields):
                newFields = fields
                var append = true
                for (index, field) in newFields.enumerate() {
                    if field.name == key {
                        newFields[index].element = newValue
                        append = false
                        break
                    }
                }
                if append {
                    newFields.append(Field(name: key, element: newValue))
                }
            default:
                newFields = [Field(name: key, element: newValue)]
            }
            self = .document(newFields)
        }
    }


    subscript(index:Int) -> BSON {
        get {
            switch self {
            case .array (let elements):
                return index < elements.count ? elements[index] : BSON.null
            default:
                return BSON.null
            }
        }
        set(newValue) {
            switch self {
            case .array (let elements):
                if index < elements.count {
                    var newElements = elements
                    newElements[index] = newValue
                    self = .array(newElements)
                }
            default:
                break
            }
        }
    }

    func encoded() -> [UInt8] {
        switch self {
        case double (let value):
            return BSON.toByteArray(value)
        case string (let value):
            let encoded = value.utf8
            return BSON.toByteArray(Int32(encoded.count + 1)) + encoded + [0]
        case document (let fields):
            var body:[UInt8] = []
            for field in fields ?? [] {
                body += [field.element.bsonType]
                body += field.name.utf8
                body += [0]
                body += field.element.encoded()
            }
            body += [0]
            return BSON.toByteArray(Int32(body.count + 4)) + body
        case array (let elements):
            var body:[UInt8] = []
            for (index, element) in elements.enumerate() {
                body += [element.bsonType]
                body += String(index).utf8
                body += [0]
                body += element.encoded()
            }
            body += [0]
            return BSON.toByteArray(Int32(body.count + 4)) + body
        case binary (let bytes):
            return BSON.toByteArray(Int32(bytes.count)) + [0x00] + bytes
//      case objectid:
//          return []
        case boolean (let value):
            return value ? [0x01] : [0x00]
        case datetime (let value):
            let since = Int64(value.timeIntervalSince1970 * 1000.0)
            return BSON.toByteArray(since)
        case null:
            return []
        case int32 (let value):
            return BSON.toByteArray(value)
        case int64 (let value):
            return BSON.toByteArray(value)
        }
    }

    static func decode(inout stream stream:IndexingGenerator<[UInt8]>, bsonType:UInt8 = 0x03) -> BSON {
        switch bsonType {
        case 0x01:
            let bytes = stream.next(sizeof(Double))
            return self.double(fromByteArray(bytes, Double.self))
        case 0x02:
            let _ = stream.next(sizeof(Int32)) // skip the count
            if let buffer = stream.upTo(0) {
                var codec = UTF8()
                if let decoded = String(codeUnits: buffer, codec: &codec) {
                    return self.string(decoded)
                }
            }
            fatalError("utf8 parse error!")
        case 0x03:
            var fields:[Field] = []
            stream.next(sizeof(Int32)) // throw out size
            while let bsonType = stream.next() {
                if bsonType == 0 {
                    return self.document(fields)
                }
                if let buffer = stream.upTo(0) {
                    var codec = UTF8()
                    if let fieldName = String(codeUnits: buffer, codec: &codec) {
                        let element = BSON.decode(stream:&stream, bsonType: bsonType)
                        fields.append(Field(name: fieldName, element: element))
                    }
                }
            }
        case 0x04:
            var elements:[BSON] = []
            stream.next(sizeof(Int32)) // throw out size
            while let bsonType = stream.next() {
                if bsonType == 0 {
                    return self.array(elements)
                }
                stream.upTo(0) // skip name
                elements.append(BSON.decode(stream:&stream, bsonType: bsonType))
            }
        case 0x05:
            let count = fromByteArray(stream.next(sizeof(Int32)), Int32.self)
            assert(stream.next() == 0x00)
            return self.binary(stream.next(Int(count)))
        case 0x07:
            break
        case 0x08:
            let value = stream.next()
            return self.boolean(value == 0x01)
        case 0x09:
            let milliseconds = fromByteArray(stream.next(sizeof(Int64)), Int64.self)
            let interval = NSTimeInterval(milliseconds) / 1000.0
            return self.datetime(NSDate(timeIntervalSince1970: interval))
        case 0x0A:
            return self.null
        case 0x10:
            let value = fromByteArray(stream.next(sizeof(Int32)), Int32.self)
            return self.int32(value)
        case 0x12:
            let value = fromByteArray(stream.next(sizeof(Int64)), Int64.self)
            return self.int64(value)
        default:
            break
        }
        fatalError()
    }

    var document:BSON? {
        switch self {
        case .document:
            return self
        default:
            return nil
        }
    }

    var double:Double? {
        switch self {
        case .double (let value):
            return value
        default:
            return nil
        }
    }

    var int32:Int32? {
        get {
            switch self {
            case .int32 (let value):
                return value
            default:
                return nil
            }
        }
        set {
            if let newValue = newValue {
                self = .int32(newValue)
            }
            else {
                self = .null
            }
        }
    }


    var int64:Int64? {
        switch self {
        case .int64 (let value):
            return value
        default:
            return nil
        }
    }

    var boolean:Bool? {
        switch self {
        case .boolean (let value):
            return value
        default:
            return nil
        }
    }

    var binary:[UInt8]? {
        switch self {
        case .binary (let value):
            return value
        default:
            return nil
        }
    }

    var string:String? {
        switch self {
        case .string (let value):
            return value
        default:
            return nil
        }
    }

    var datetime:NSDate? {
        switch self {
        case .datetime (let value):
            return value
        default:
            return nil
        }
    }

    var array:[BSON]? {
        switch self {
        case .array (let elements):
            return elements
        default:
            return nil
        }
    }

    var isNull:Bool {
        switch self {
        case .null:
            return true
        default:
            return false
        }
    }

    var keyValues:[(String, BSON)] {
        switch self {
        case document (let fields):
            return fields.map() { ($0.name, $0.element) }
        default:
            return []
        }
    }

}

extension BSON: Equatable {}
extension BSON.Field: Equatable {}

func == (a:BSON.Field, b:BSON.Field) -> Bool {
    return a.name == b.name && a.element == b.element
}

func == (a:BSON, b:BSON) -> Bool {
    switch (a, b) {
    case (.double(let a),   .double(let b))   where a == b:             return true
    case (.string(let a),   .string(let b))   where a == b:             return true
    case (.document(let a), .document(let b)) where a == b:             return true
    case (.array(let a),    .array(let b))    where a == b:             return true
    case (.binary(let a),   .binary(let b))   where a == b:             return true
    case (.boolean(let a),  .boolean(let b))  where a == b:             return true
    case (.datetime(let a), .datetime(let b)) where a.isEqualToDate(b): return true
    case (.null,            .null):                                     return true
    case (.int32(let a),    .int32(let b))    where a == b:             return true
    case (.int64(let a),    .int64(let b))    where a == b:             return true
    default: return false
    }
}

protocol BSONConvertable {
    var bson:BSON { get }
}

extension Int32: BSONConvertable {
    var bson:BSON {
        return BSON.int32(self)
    }
}

extension Int64: BSONConvertable {
    var bson:BSON {
        return BSON.int64(self)
    }
}

extension Int: BSONConvertable {
    var bson:BSON {
        let wide = self.toIntMax()
        if Int32.min.toIntMax() <= wide && wide <= Int32.max.toIntMax() {
            return BSON.int32(Int32(wide))
        }
        else {
            return BSON.int64(Int64(wide))
        }
    }
}


extension Float:BSONConvertable {
    var bson:BSON {
        return Double(self).bson
    }
}

extension Double:BSONConvertable {
    var bson:BSON {
        return BSON.double(self)
    }
}

extension Bool:BSONConvertable {
    var bson:BSON {
        return BSON.boolean(self)
    }
}

extension BSON:BSONConvertable {
    var bson:BSON {
        return self
    }
}

extension NSDate:BSONConvertable {
    var bson:BSON {
        return BSON.datetime(self)
    }
}

extension String:BSONConvertable {
    var bson:BSON {
        return BSON.string(self)
    }
}

extension Array where Element:BSONConvertable {
    var bson:BSON {
        return BSON.array(self.map({$0.bson}))
    }
}

关于swift - swift 的BSON?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34074538/

有关swift - swift 的BSON?的更多相关文章

  1. ruby - MongoDB:无法从 BSON 类型 EOO 转换为 Date - 2

    我正在尝试使用聚合框架(使用ruby​​)并像这样投影日期:db['requests'].aggregate([{"$project"=>{_id:0,method:'$method',user:'$user',year:{'$year'=>'$timestamp'}}}])文档是这样的:{_id:ObjectId("5177d7d7df26358289da7dfd"),timestamp:ISODate("2013-04-12T03:58:05+00:00"),method:"POST",status:"200",inputsize:"874",outputsize:"4981",u

  2. mongodb - filter:= bson.D {{“hello”,“world”}}}而不是使用value(world)如何传递包含该值的变量(world) - 2

    Closed.Thisquestionneedsdetailsorclarity。它当前不接受答案。想改善这个问题吗?添加详细信息,并通过editingthispost阐明问题。去年关闭。Improvethisquestion使用Go从mongodb获取特定内容时,例如:filter:=bson.D{{"hello","world"}}在这种情况下,我如何传递包含值(世界)的变量而不是传递值(世界)?username:=r.FormValue("username")filter:=bson.D{{"username",'$username'}} 最佳答案

  3. swift - 将 json 编码时间转换为 nsdate - 2

    当我将time.Now()编码到JSON对象时,它给出的结果为"2009-11-10T23:00:00Z"但打印时间。现在给出2009-11-1023:00:00+0000UTC。他们为什么不同。什么是T和Z。另外,如何根据this将其转换为swiftNSDate对象?表? 最佳答案 这些值的含义无关紧要,它们是该格式(ISO8601)的一部分。有几种方法可以解决这个问题。一种是为时间或您的结构定义自定义MarshalJSON()方法并使用它来格式化日期,另一种是首先在您的结构中将其表示为字符串,以便当默认实现执行你得到你正在寻找的

  4. go - 如果我的 key 都被命名为 "Key",我如何从 BSON 获取 JSON? - 2

    我正在尝试从数据库中读取数据,然后将结果作为json返回给用户。发生的事情是我得到如下输出:[{"Key":"foo","Value":"bar"}]当我想得到:"{"foo":"bar"}"我如何获得前者?示例:(未显示从数据库读取并将Raw转换为字符串)packagemainimport("encoding/json""fmt""go.mongodb.org/mongo-driver/bson")funcmain(){vardata="{\"foo\":\"bar\"}"vartestInterfaceinterface{}e:=bson.UnmarshalExtJSON([]by

  5. MongoDB bson.M 查询 - 2

    我正在尝试使用野牛查询MongoDB中带有两个字段的所有JSON数据,但结果为空。{"allowedList":[{"List":[{"allow":{"ss":1,},"Information":[{"Id":"Id1"}]}]}]}我能够在命令行使用MongoDB过滤所有内容db.slicedb.find({"allowedList.List.allow.ss":1,"allowedList.List.Information.nsiId":"Id-Id21"})butusingquery:=bson.M{"allowedList.List.allow":bson.M{"ss":ss

  6. regex - 如何在我的 go 项目中修复此错误 - undefined : bson. RegEx - 2

    我的编辑器出现以下错误:未定义:bson.RegEx由于我的go项目中的这行代码:regex:=bson.M{"$regex":bson.RegEx{模式:id,选项:"i"}}为什么会出现此错误以及如何解决?我已确定我正在导入:"go.mongdb.org/mongo-driver/bson"我还检查了bson/primitive/primitive.go以查看RegEx确实存在使用版本1.1.0的mongo-driver。 最佳答案 设法通过删除这个来解决这个问题:regex:=bson.M{"$regex":bson.RegE

  7. regex - 如何解决关于 'filter'字段必须是BSON类型对象的问题 - 2

    我想在调用查找查询时解决这个问题。这是运行Golang,并使用包“”gopkg.in/mgo.v2/bson”。import"gopkg.in/mgo.v2"import"fmt"/*mongodb*/info:=&mgo.DialInfo{Addrs:[]string{1.1.1.1+":"+27017},Database:MgName,Username:MgId,Password:MgPasswd,}mgconn,err:=mgo.DialWithInfo(info)iferr!=nil{fmt.Printf("[ERR]mongodb:%s\n",err)return(-1)}/

  8. mongodb - 如何导入go的mongo-driver bson - 2

    我使用gogetgo.mongodb.org/mongo-driver/...安装了mongogo驱动程序,但是每当我尝试使用bson.EC或bson.NewDocument如图所示here,我收到错误:undefined:bson.NewDocument和undefined:bson.EC。我在这里缺少什么?import("go.mongodb.org/mongo-driver/bson")funcmain(){//databaseandcollectionconnection//...filter:=bson.NewDocument(bson.EC.String("_id","fo

  9. go - 如何编写 Golang bson- MongoDB - 2

    我正在尝试为这个mongodb查询编写golangbson查询但不能。谁能帮忙?我可以使用命令查询mongoshelldb.collection.find({"nfType":"SMF"},{"_id":0,"ipv4Addresses":1})它给出了我想要的输出[{"ipv4Addresses":["198.51.100.1"]}]现在我正在尝试为此查询编写一个golangbson以仅获取上面显示的ipv4Addresses字段但不能。集合中的文档的形式为{"nfType":["SMF"],"nfStatus":["REG"],"sNssais":[{"sst":1,"sd":"s

  10. json - 如何使用mongo-go-driver有效地将bson转换为json? - 2

    我想在mongo-go-driver中转换bson有效地转换为json。我应该注意处理NaN,因为如果NaN存在于数据中,json.Marshal就会失败。比如我想把下面的bson数据转成json。b,_:=bson.Marshal(bson.M{"a":[]interface{}{math.NaN(),0,1}})//Howtoconvertbtojson?以下失败。//decodevardecodedBsonbson.Mbson.Unmarshal(b,&decodedBson)_,err:=json.Marshal(decodedBson)iferr!=nil{panic(err

随机推荐