BSON for Swift?

时间:2015-12-03 19:29:44

标签: swift bson

我的问题在这一点上非常开放,但我很好奇是否有人在Swift中为BSON实现了类似SwiftyJSON的东西?

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

任何人都是

A)知道我的谷歌搜索尚未发现的事情或

B)或者可以帮助我朝着正确的方向推动我如何一般地设计这样的东西? (不是试图让别人为我工作)

除了:预防“你为什么不使用json”问题...这是因为我在另一边做了很多MongoDB而且我使用了很多字符串和日期,必须在JSON

中含糊不清

2 个答案:

答案 0 :(得分:1)

为了关闭...我最终写了自己的。它不是所有BSON编码的完整解决方案,只是我使用的那些。用Swift Enums弄清楚如何做到这一点很有趣。

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}))
    }
}

答案 1 :(得分:1)

晚会,但github上的OpenKitten实现似乎是一个很好的实现,它是从BSON规范网站引用的。 https://github.com/OpenKitten/BSON