Skip to content

Commit b876c31

Browse files
committed
Streamlined conversions to JSON types
1 parent 33da423 commit b876c31

File tree

2 files changed

+86
-24
lines changed

2 files changed

+86
-24
lines changed

Sources/Turf/JSON.swift

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ public enum JSONValue: Equatable {
2727
/// An object containing JSON values and `null` values keyed by strings.
2828
case object(_ properties: JSONObject)
2929

30-
/// Initializes a JSON value representing the given string.
31-
public init(_ string: String) {
32-
self = .string(string)
30+
/// Initializes a JSON value representing the given JSON value–convertible instance.
31+
public init(_ value: JSONValueConvertible) {
32+
self = value.jsonValue
3333
}
3434

3535
/**
@@ -38,27 +38,22 @@ public enum JSONValue: Equatable {
3838
- parameter number: An integer. JSON does not distinguish numeric types of different precisions, so the integer is stored as a floating-point number.
3939
*/
4040
public init<Source>(_ number: Source) where Source: BinaryInteger {
41-
self = .number(Double(number))
41+
self.init(Double(number))
4242
}
4343

4444
/// Initializes a JSON value representing the given floating-point number.
4545
public init<Source>(_ number: Source) where Source: BinaryFloatingPoint {
46-
self = .number(Double(number))
46+
self.init(Double(number))
4747
}
4848

49-
/// Initializes a JSON value representing the given Boolean value.
50-
public init(_ bool: Bool) {
51-
self = .boolean(bool)
49+
/// Initializes a JSON value representing the given JSON-convertible array.
50+
public init(_ values: [JSONValueConvertible?]) {
51+
self = .array(JSONArray(values))
5252
}
5353

54-
/// Initializes a JSON value representing the given JSON array.
55-
public init(_ values: JSONArray) {
56-
self = .array(values)
57-
}
58-
59-
/// Initializes a JSON value representing the given JSON object.
60-
public init(_ properties: JSONObject) {
61-
self = .object(properties)
54+
/// Initializes a JSON value representing the given JSON-convertible object.
55+
public init(_ properties: [String: JSONValueConvertible?]) {
56+
self = .object(JSONObject(properties))
6257
}
6358
}
6459

@@ -105,6 +100,13 @@ extension JSONValue: RawRepresentable {
105100
*/
106101
public typealias JSONArray = [JSONValue?]
107102

103+
extension JSONArray {
104+
public init(_ elements: [JSONValueConvertible?]) {
105+
let values = elements.map { $0?.jsonValue }
106+
self.init(values)
107+
}
108+
}
109+
108110
extension JSONArray: RawRepresentable {
109111
public typealias RawValue = [Any?]
110112

@@ -122,6 +124,12 @@ extension JSONArray: RawRepresentable {
122124
*/
123125
public typealias JSONObject = [String: JSONValue?]
124126

127+
extension JSONObject {
128+
public init(_ elements: [String: JSONValueConvertible?]) {
129+
self.init(uniqueKeysWithValues: elements.map { ($0.0, $0.1?.jsonValue) })
130+
}
131+
}
132+
125133
extension JSONObject: RawRepresentable {
126134
public typealias RawValue = [String: Any?]
127135

@@ -136,42 +144,42 @@ extension JSONObject: RawRepresentable {
136144

137145
extension JSONValue: ExpressibleByStringLiteral {
138146
public init(stringLiteral value: StringLiteralType) {
139-
self = .init(value)
147+
self.init(value)
140148
}
141149
}
142150

143151
extension JSONValue: ExpressibleByIntegerLiteral {
144152
public init(integerLiteral value: IntegerLiteralType) {
145-
self = .init(value)
153+
self.init(value)
146154
}
147155
}
148156

149157
extension JSONValue: ExpressibleByFloatLiteral {
150158
public init(floatLiteral value: FloatLiteralType) {
151-
self = .init(value)
159+
self.init(value)
152160
}
153161
}
154162

155163
extension JSONValue: ExpressibleByBooleanLiteral {
156164
public init(booleanLiteral value: BooleanLiteralType) {
157-
self = .init(value)
165+
self.init(value)
158166
}
159167
}
160168

161169
extension JSONValue: ExpressibleByArrayLiteral {
162-
public typealias ArrayLiteralElement = JSONValue?
170+
public typealias ArrayLiteralElement = JSONValueConvertible?
163171

164172
public init(arrayLiteral elements: ArrayLiteralElement...) {
165-
self = .init(elements)
173+
self.init(elements)
166174
}
167175
}
168176

169177
extension JSONValue: ExpressibleByDictionaryLiteral {
170178
public typealias Key = String
171-
public typealias Value = JSONValue?
179+
public typealias Value = JSONValueConvertible?
172180

173181
public init(dictionaryLiteral elements: (Key, Value)...) {
174-
self = .init(.init(uniqueKeysWithValues: elements))
182+
self = .object(JSONObject(uniqueKeysWithValues: elements.map { ($0.0, $0.1?.jsonValue) }))
175183
}
176184
}
177185

@@ -209,3 +217,39 @@ extension JSONValue: Codable {
209217
}
210218
}
211219
}
220+
221+
/**
222+
A type that can be represented as a `JSONValue` instance.
223+
*/
224+
public protocol JSONValueConvertible {
225+
/// The instance wrapped in a `JSONValue` instance.
226+
var jsonValue: JSONValue { get }
227+
}
228+
229+
extension String: JSONValueConvertible {
230+
public var jsonValue: JSONValue { return .string(self) }
231+
}
232+
233+
extension Int: JSONValueConvertible {
234+
public var jsonValue: JSONValue { return .number(Double(self)) }
235+
}
236+
237+
extension Double: JSONValueConvertible {
238+
public var jsonValue: JSONValue { return .number(Double(self)) }
239+
}
240+
241+
extension Bool: JSONValueConvertible {
242+
public var jsonValue: JSONValue { return .boolean(self) }
243+
}
244+
245+
extension Array: JSONValueConvertible where Element == JSONValueConvertible? {
246+
public var jsonValue: JSONValue {
247+
return .array(map { $0?.jsonValue })
248+
}
249+
}
250+
251+
extension Dictionary: JSONValueConvertible where Key == String, Value == JSONValueConvertible? {
252+
public var jsonValue: JSONValue {
253+
return .object(JSONObject(uniqueKeysWithValues: map { ($0.0, $0.1?.jsonValue) }))
254+
}
255+
}

Tests/TurfTests/JSONTests.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ class JSONTests: XCTestCase {
4444
XCTAssertEqual(JSONObject(rawValue: ["set": Set(["Get"])]), ["set": nil])
4545
}
4646

47+
func testConversion() {
48+
XCTAssertEqual(JSONValue(String("Jason")), .string("Jason"))
49+
XCTAssertEqual(JSONValue(Int32.max), .number(Double(Int32.max)))
50+
XCTAssertEqual(JSONValue(Float(0.0).nextUp), .number(Double(Float(0.0).nextUp)))
51+
XCTAssertEqual(JSONValue(0.0.nextUp), .number(0.0.nextUp))
52+
XCTAssertEqual(JSONValue(Bool(true)), .boolean(true))
53+
XCTAssertEqual(JSONValue(Bool(false)), .boolean(false))
54+
55+
let array = "Jason".map(String.init) + [nil]
56+
XCTAssertEqual(JSONValue(array), .array(["J", "a", "s", "o", "n", nil]))
57+
let dictionary = ["string": "Jason", "null": nil]
58+
XCTAssertEqual(JSONValue(dictionary), .object(["string": "Jason", "null": nil]))
59+
60+
XCTAssertEqual(JSONArray("Jason".map(\.description)), ["J", "a", "s", "o", "n"])
61+
XCTAssertEqual(JSONArray(array), ["J", "a", "s", "o", "n", nil])
62+
XCTAssertEqual(JSONObject(dictionary), ["string": "Jason", "null": nil])
63+
}
64+
4765
func testLiterals() throws {
4866
if case let JSONValue.string(string) = "Jason" {
4967
XCTAssertEqual(string, "Jason")

0 commit comments

Comments
 (0)