Skip to content

Commit 2218e1b

Browse files
authored
SWIFT-925 Implement ExtendedJSON Encoder API (#32)
1 parent fe49ffc commit 2218e1b

File tree

2 files changed

+77
-17
lines changed

2 files changed

+77
-17
lines changed

Sources/BSON/ExtendedJSONEncoder.swift

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ public class ExtendedJSONEncoder {
2121
public var userInfo: [CodingUserInfoKey: Any] = [:]
2222

2323
/// Initialize an `ExtendedJSONEncoder`.
24-
public init() {
25-
fatalError("unimplemented")
26-
}
24+
public init() {}
2725

2826
/// Encodes an instance of the Encodable Type `T` into Data representing canonical or relaxed extended JSON.
2927
/// The value of `self.mode` will determine which format is used. If it is not set explicitly, relaxed will be used.
@@ -40,6 +38,18 @@ public class ExtendedJSONEncoder {
4038
// The `BSON` is converted to an instance of the `JSON` enum via the `toRelaxedExtendedJSON`
4139
// or `toCanonicalExtendedJSON` methods on `BSONValue`s (depending on the `mode`).
4240
// The `JSON` is then passed through a `JSONEncoder` and outputted as `Data`.
43-
fatalError("unimplemented")
41+
let encoder = BSONEncoder()
42+
encoder.userInfo = self.userInfo
43+
let bson: BSON = try encoder.encode(value)
44+
45+
let json: JSON
46+
switch self.mode {
47+
case .canonical:
48+
json = bson.bsonValue.toCanonicalExtendedJSON()
49+
case .relaxed:
50+
json = bson.bsonValue.toRelaxedExtendedJSON()
51+
}
52+
53+
return try JSONEncoder().encode(json)
4454
}
4555
}

Tests/BSONTests/ExtendedJSONConversionTests.swift

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,70 @@ import Nimble
44
import NIO
55
import XCTest
66

7+
extension CodingUserInfoKey {
8+
static let barInfo = CodingUserInfoKey(rawValue: "bar")!
9+
}
10+
711
open class ExtendedJSONConversionTestCase: BSONTestCase {
12+
func testExtendedJSONDecoderAndEncoder() throws {
13+
// Setup
14+
struct Test: Codable, Equatable {
15+
let x: Bool
16+
let y: Int32
17+
let z: BSONRegularExpression
18+
}
19+
20+
let regexStr = "{\"$regularExpression\":{\"pattern\":\"p\",\"options\":\"i\"}}"
21+
let extJSON = "{\"x\":true,\"y\":{\"$numberInt\":\"5\"},\"z\":\(regexStr)}"
22+
let data = extJSON.data(using: .utf8)!
23+
let regexObj = BSONRegularExpression(pattern: "p", options: "i")
24+
let test = Test(x: true, y: 5, z: regexObj)
25+
26+
// Test canonical encoder
27+
let encoder = ExtendedJSONEncoder()
28+
encoder.mode = .canonical
29+
let encoded: Data = try encoder.encode(test)
30+
31+
let encodedStr = String(data: encoded, encoding: .utf8)
32+
expect(encodedStr).to(contain("\"x\":true"))
33+
expect(encodedStr).to(contain("\"y\":{\"$numberInt\":\"5\"}"))
34+
expect(encodedStr).to(contain("\"z\":{\"$regularExpression\""))
35+
expect(encodedStr).to(contain("\"pattern\":\"p\""))
36+
expect(encodedStr).to(contain("\"options\":\"i\""))
37+
38+
// Test relaxed encoder
39+
encoder.mode = .relaxed
40+
let relaxedEncoded: Data = try encoder.encode(test)
41+
let relaxedEncodedStr = String(data: relaxedEncoded, encoding: .utf8)
42+
expect(relaxedEncodedStr).to(contain("\"x\":true"))
43+
expect(relaxedEncodedStr).to(contain("\"y\":5"))
44+
45+
// Test decoder
46+
let decoder = ExtendedJSONDecoder()
47+
let decoded = try decoder.decode(Test.self, from: data)
48+
expect(decoded).to(equal(test))
49+
}
50+
51+
func testExtendedJSONEncodingWithUserInfo() throws {
52+
struct Foo: Codable, Equatable {
53+
func encode(to encoder: Encoder) throws {
54+
let barInfo = encoder.userInfo[.barInfo] as? Bool
55+
var container = encoder.singleValueContainer()
56+
try container.encode([barInfo])
57+
}
58+
}
59+
60+
let encoder = ExtendedJSONEncoder()
61+
62+
encoder.userInfo[.barInfo] = true
63+
let fooBarEncoded = try encoder.encode(Foo())
64+
expect(String(data: fooBarEncoded, encoding: .utf8)).to(contain("true"))
65+
66+
encoder.userInfo[.barInfo] = false
67+
let fooEncoded = try encoder.encode(Foo())
68+
expect(String(data: fooEncoded, encoding: .utf8)).to(contain("false"))
69+
}
70+
871
func testAnyExtJSON() throws {
972
// Success cases
1073
expect(try BSON(fromExtJSON: "hello", keyPath: [])).to(equal(BSON.string("hello")))
@@ -14,19 +77,6 @@ open class ExtendedJSONConversionTestCase: BSONTestCase {
1477
expect(document.documentValue!["extra"]).to(equal(.int32(1)))
1578
}
1679

17-
func testExtendedJSONDecoder() throws {
18-
struct Foo: Decodable, Equatable {
19-
let x: Bool
20-
let y: Int
21-
let z: BSONRegularExpression
22-
}
23-
let regexStr = "{\"$regularExpression\": {\"pattern\": \"p\", \"options\": \"i\"}}"
24-
let regexObj = BSONRegularExpression(pattern: "p", options: "i")
25-
let data = "{ \"x\": true, \"y\": { \"$numberInt\": \"5\" }, \"z\": \(regexStr) }".data(using: .utf8)!
26-
let decoder = ExtendedJSONDecoder()
27-
expect(try decoder.decode(Foo.self, from: data)).to(equal(Foo(x: true, y: 5, z: regexObj)))
28-
}
29-
3080
func testObjectId() throws {
3181
let oid = "5F07445CFBBBBBBBBBFAAAAA"
3282

0 commit comments

Comments
 (0)