Skip to content

Commit a7389e3

Browse files
authored
SWIFT-959 Support userInfo in ExtendedJSONDecoder (#37)
1 parent cfbe1a2 commit a7389e3

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

Sources/BSON/ExtendedJSONDecoder.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public class ExtendedJSONDecoder {
1313
return formatter
1414
}()
1515

16+
/// Contextual user-provided information for use during decoding.
17+
public var userInfo: [CodingUserInfoKey: Any] = [:]
18+
1619
/// Initialize an `ExtendedJSONDecoder`.
1720
public init() {}
1821

@@ -28,11 +31,18 @@ public class ExtendedJSONDecoder {
2831
// Data --> JSON --> BSON --> T
2932
// Takes in JSON as `Data` encoded with `.utf8` and runs it through a `JSONDecoder` to get an
3033
// instance of the `JSON` enum.
34+
35+
// In earlier versions of Swift, JSONDecoder doesn't support decoding "fragments" at the top level, so we wrap
36+
// the data in an array to guarantee it always decodes properly.
3137
let wrappedData = "[".utf8 + data + "]".utf8
3238
let json = try JSONDecoder().decode([JSON].self, from: wrappedData)[0]
39+
3340
// Then a `BSON` enum instance is created via the `JSON`.
3441
let bson = try BSON(fromExtJSON: json, keyPath: [])
42+
3543
// The `BSON` is then passed through a `BSONDecoder` where it is outputted as a `T`
36-
return try BSONDecoder().decode(T.self, fromBSON: bson)
44+
let bsonDecoder = BSONDecoder()
45+
bsonDecoder.userInfo = self.userInfo
46+
return try bsonDecoder.decode(T.self, fromBSON: bson)
3747
}
3848
}

Tests/BSONTests/ExtendedJSONConversionTests.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,48 @@ open class ExtendedJSONConversionTestCase: BSONTestCase {
4141
expect(decoded).to(equal(test))
4242
}
4343

44+
func testExtendedJSONDecodingWithUserInfo() throws {
45+
struct Foo: Decodable, Equatable {
46+
let val: BSON
47+
let bar: Bar
48+
49+
init(from decoder: Decoder) throws {
50+
guard let info = decoder.userInfo[.barInfo] as? BSON else {
51+
throw TestError(message: "userInfo not present")
52+
}
53+
self.val = info
54+
55+
// test userinfo is propogated to sub containers
56+
let container = try decoder.singleValueContainer()
57+
self.bar = try container.decode(Bar.self)
58+
}
59+
}
60+
61+
struct Bar: Decodable, Equatable {
62+
let val: BSON
63+
64+
init(from decoder: Decoder) throws {
65+
guard let info = decoder.userInfo[.barInfo] as? BSON else {
66+
throw TestError(message: "userInfo not present")
67+
}
68+
self.val = info
69+
}
70+
}
71+
72+
let obj = "{}".data(using: .utf8)!
73+
let decoder = ExtendedJSONDecoder()
74+
75+
decoder.userInfo[.barInfo] = BSON.bool(true)
76+
let boolDecoded = try decoder.decode(Foo.self, from: obj)
77+
expect(boolDecoded.val).to(equal(true))
78+
expect(boolDecoded.bar.val).to(equal(true))
79+
80+
decoder.userInfo[.barInfo] = BSON.string("hello world")
81+
let stringDecoded = try decoder.decode(Foo.self, from: obj)
82+
expect(stringDecoded.val).to(equal("hello world"))
83+
expect(stringDecoded.bar.val).to(equal("hello world"))
84+
}
85+
4486
func testExtendedJSONEncodingWithUserInfo() throws {
4587
struct Foo: Codable, Equatable {
4688
func encode(to encoder: Encoder) throws {

0 commit comments

Comments
 (0)