2015-12-03 4 views
1

Мой вопрос довольно открытый закончился в этот момент, но мне любопытно, если кто-то реализовал что-то вроде SwiftyJSON для BSON в Swift?BSON для Swift?

Я обнаружил, что проект Perfect имеет что-то, но он, кажется, просто обертка вокруг существующей библиотеки C (мне не будет полезно на стороне iOS). Я играл с просто портированием/деформированием SwiftyJSON, внутренности которого немного выше моей кривой обучения до сих пор. И это, похоже, просто обертка вокруг платформы JSONSerialization.

Так кто либо

A) знают что-то сделать, что моя прибегая к помощи до сих пор не обнаружена или

B) или может помочь подтолкнуть меня в правильном направлении, с тем, как в целом архитектора такой вещи ? (не пытайтесь заставить других работать для меня)

в сторону: чтобы избежать вопросов «почему бы вам просто не использовать json» ... это потому, что я делаю совсем немного MongoDB на другом и я работаю со многими строками и датами, которые должны быть неоднозначно представлены в JSON.

+1

Что случилось с использованием библиотеки C на iOS? Взаимодействие между Swift и C в целом довольно хорошо. –

+1

Во-вторых, если mongodb - это серверный сервер, кажется очень плохой практикой выставлять формат сериализации и требовать, чтобы клиенты придерживались этого. Если вы замените манго на что-то еще, вам придется реорганизовать всех своих клиентов. –

+0

Ничего плохого в использовании библиотеки C в iOS. Еще не сделали этого, поэтому не знакомы с интеграцией этих двух. Кроме того, это казалось хорошей возможностью обучения. –

ответ

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

FWIW, я сейчас пишу класс BSONSerialization с открытым исходным кодом в чистом виде. Наслаждайтесь! https://github.com/Frizlab/BSONSerialization – Frizlab

Смежные вопросы