此时我的问题很开放,但我很好奇是否有人在 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/
我正在尝试使用聚合框架(使用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
Closed.Thisquestionneedsdetailsorclarity。它当前不接受答案。想改善这个问题吗?添加详细信息,并通过editingthispost阐明问题。去年关闭。Improvethisquestion使用Go从mongodb获取特定内容时,例如:filter:=bson.D{{"hello","world"}}在这种情况下,我如何传递包含值(世界)的变量而不是传递值(世界)?username:=r.FormValue("username")filter:=bson.D{{"username",'$username'}} 最佳答案
当我将time.Now()编码到JSON对象时,它给出的结果为"2009-11-10T23:00:00Z"但打印时间。现在给出2009-11-1023:00:00+0000UTC。他们为什么不同。什么是T和Z。另外,如何根据this将其转换为swiftNSDate对象?表? 最佳答案 这些值的含义无关紧要,它们是该格式(ISO8601)的一部分。有几种方法可以解决这个问题。一种是为时间或您的结构定义自定义MarshalJSON()方法并使用它来格式化日期,另一种是首先在您的结构中将其表示为字符串,以便当默认实现执行你得到你正在寻找的
我正在尝试从数据库中读取数据,然后将结果作为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
我正在尝试使用野牛查询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
我的编辑器出现以下错误:未定义: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
我想在调用查找查询时解决这个问题。这是运行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)}/
我使用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
我正在尝试为这个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
我想在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