Skip to content

Commit cfbe1a2

Browse files
authored
SWIFT-930 Implement existing ExtendedJSON API (#36)
1 parent 2dfa1b5 commit cfbe1a2

File tree

5 files changed

+386
-311
lines changed

5 files changed

+386
-311
lines changed

Sources/BSON/BSONDecoder.swift

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -177,38 +177,26 @@ public class BSONDecoder {
177177
}
178178
}
179179

180-
// TODO: SWIFT-930 Implement this
181-
// /**
182-
// * Decodes a top-level value of the given type from the given JSON/extended JSON string.
183-
// *
184-
// * - Parameter type: The type of the value to decode.
185-
// * - Parameter json: The JSON string to decode from.
186-
// * - Returns: A value of the requested type.
187-
// * - Throws: `DecodingError` if the JSON data is corrupt or if any value throws an error during decoding.
188-
// */
189-
// public func decode<T: Decodable>(_: T.Type, from json: String) throws -> T {
190-
// // we nest the input JSON in another object, and then decode to a `DecodableWrapper`
191-
// // wrapping an object of the requested type. since our decoder only supports decoding
192-
// // objects, this allows us to additionally handle decoding to primitive types like a
193-
// // `String` or an `Int`.
194-
// // while this is not needed to decode JSON representing objects, it is difficult to
195-
// // determine when JSON represents an object vs. a primitive value -- for example,
196-
// // {"$numberInt": "42"} is a JSON object and looks like an object type but is actually
197-
// // a primitive type, Int32. so for simplicity, we just always assume wrapping is needed,
198-
// // and pay a small performance penalty of decoding a few extra bytes.
199-
// let wrapped = "{\"value\": \(json)}"
200-
201-
// if let doc = try? BSONDocument(fromJSON: wrapped) {
202-
// let s = try self.decode(DecodableWrapper<T>.self, from: doc)
203-
// return s.value
204-
// }
205-
206-
// throw DecodingError.dataCorrupted(
207-
// DecodingError.Context(
208-
// codingPath: [],
209-
// debugDescription: "Unable to parse JSON string \(json)"
210-
// ))
211-
// }
180+
/**
181+
* Decodes a top-level value of the given type from the given JSON/extended JSON string.
182+
*
183+
* - Parameter type: The type of the value to decode.
184+
* - Parameter json: The JSON string to decode from.
185+
* - Returns: A value of the requested type.
186+
* - Throws: `DecodingError` if the JSON data is corrupt or if any value throws an error during decoding.
187+
*/
188+
@available(*, deprecated, message: "Use ExtendedJSONDecoder.decode instead")
189+
public func decode<T: Decodable>(_: T.Type, from json: String) throws -> T {
190+
let decoder = ExtendedJSONDecoder()
191+
guard let jsonData = json.data(using: .utf8) else {
192+
throw DecodingError.dataCorrupted(
193+
DecodingError.Context(
194+
codingPath: [],
195+
debugDescription: "Unable to parse JSON string \(json)"
196+
))
197+
}
198+
return try decoder.decode(T.self, from: jsonData)
199+
}
212200

213201
/// A struct to wrap a `Decodable` type, allowing us to support decoding to types that
214202
/// are not inside a wrapping object (for ex., Int or String).

Sources/BSON/BSONDocument.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,50 @@ public struct BSONDocument {
115115
}
116116
}
117117

118+
/**
119+
* Constructs a new `BSONDocument` from the provided JSON text.
120+
*
121+
* - Parameters:
122+
* - fromJSON: a JSON document as `Data` to parse into a `BSONDocument`
123+
*
124+
* - Returns: the parsed `BSONDocument`
125+
*
126+
* - Throws: `DecodingError` if `json` is a partial match or is malformed.
127+
*/
128+
public init(fromJSON json: Data) throws {
129+
let decoder = ExtendedJSONDecoder()
130+
self = try decoder.decode(BSONDocument.self, from: json)
131+
}
132+
133+
/// Convenience initializer for constructing a `BSONDocument` from a `String`.
134+
/// - Throws: `DecodingError` if `json` is a partial match or is malformed.
135+
public init(fromJSON json: String) throws {
136+
// `String`s are Unicode under the hood so force unwrap always succeeds.
137+
// see https://www.objc.io/blog/2018/02/13/string-to-data-and-back/
138+
try self.init(fromJSON: json.data(using: .utf8)!) // swiftlint:disable:this force_unwrapping
139+
}
140+
141+
/// Returns the relaxed extended JSON representation of this `BSONDocument`.
142+
/// On error, an empty string will be returned.
143+
public func toExtendedJSONString() -> String {
144+
let encoder = ExtendedJSONEncoder()
145+
guard let encoded = try? encoder.encode(self) else {
146+
return ""
147+
}
148+
return String(data: encoded, encoding: .utf8) ?? ""
149+
}
150+
151+
/// Returns the canonical extended JSON representation of this `BSONDocument`.
152+
/// On error, an empty string will be returned.
153+
public func toCanonicalExtendedJSONString() -> String {
154+
let encoder = ExtendedJSONEncoder()
155+
encoder.mode = .canonical
156+
guard let encoded = try? encoder.encode(self) else {
157+
return ""
158+
}
159+
return String(data: encoded, encoding: .utf8) ?? ""
160+
}
161+
118162
/// The keys in this `BSONDocument`.
119163
public var keys: [String] { self.map { key, _ in key } }
120164

0 commit comments

Comments
 (0)