Skip to content

Commit

Permalink
Support Codable
Browse files Browse the repository at this point in the history
  • Loading branch information
Lei Wang committed Mar 28, 2018
1 parent 6bbffd3 commit 38d9815
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 15 deletions.
1 change: 0 additions & 1 deletion Example/Playground.playground/Contents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -413,4 +413,3 @@ let stringRepresentionJson: JSON = JSON(stringRepresentationDict)
let representation = stringRepresentionJson.rawString([.castNilToNSNull: true])
print(representation!)
// representation is "{\"1\":2,\"2\":\"two\",\"3\":null}", which represents {"1":2,"2":"two","3":null}

134 changes: 120 additions & 14 deletions Source/SwiftyJSON.swift
Original file line number Diff line number Diff line change
Expand Up @@ -545,53 +545,50 @@ extension JSON {
extension JSON: Swift.ExpressibleByStringLiteral {

public init(stringLiteral value: StringLiteralType) {
self.init(value as Any)
self.init(value)
}

public init(extendedGraphemeClusterLiteral value: StringLiteralType) {
self.init(value as Any)
self.init(value)
}

public init(unicodeScalarLiteral value: StringLiteralType) {
self.init(value as Any)
self.init(value)
}
}

extension JSON: Swift.ExpressibleByIntegerLiteral {

public init(integerLiteral value: IntegerLiteralType) {
self.init(value as Any)
self.init(value)
}
}

extension JSON: Swift.ExpressibleByBooleanLiteral {

public init(booleanLiteral value: BooleanLiteralType) {
self.init(value as Any)
self.init(value)
}
}

extension JSON: Swift.ExpressibleByFloatLiteral {

public init(floatLiteral value: FloatLiteralType) {
self.init(value as Any)
self.init(value)
}
}

extension JSON: Swift.ExpressibleByDictionaryLiteral {
public init(dictionaryLiteral elements: (String, Any)...) {
var dictionary = [String: Any](minimumCapacity: elements.count)
for (k, v) in elements {
dictionary[k] = v
}
self.init(dictionary as Any)
let dictionary = elements.reduce(into: [String: Any](), { $0[$1.0] = $1.1})
self.init(dictionary)
}
}

extension JSON: Swift.ExpressibleByArrayLiteral {

public init(arrayLiteral elements: Any...) {
self.init(elements as Any)
self.init(elements)
}
}

Expand Down Expand Up @@ -776,7 +773,7 @@ extension JSON {
}
set {
if let array = newValue {
self.object = array as Any
self.object = array
} else {
self.object = NSNull()
}
Expand Down Expand Up @@ -819,7 +816,7 @@ extension JSON {
}
set {
if let v = newValue {
self.object = v as Any
self.object = v
} else {
self.object = NSNull()
}
Expand Down Expand Up @@ -1455,3 +1452,112 @@ public enum writingOptionsKeys {
case maxObjextDepth
case encoding
}

// MARK: - JSON: Codable
extension JSON: Codable {
private static var codableTypes: [Codable.Type] {
return [
Bool.self,
Int.self,
Int8.self,
Int16.self,
Int32.self,
Int64.self,
UInt.self,
UInt8.self,
UInt16.self,
UInt32.self,
UInt64.self,
Double.self,
String.self,
[JSON].self,
[String: JSON].self
]
}
public init(from decoder: Decoder) throws {
var object: Any?

if let container = try? decoder.singleValueContainer(), !container.decodeNil() {
for type in JSON.codableTypes {
if object != nil {
break
}
// try to decode value
switch type {
case let boolType as Bool.Type:
object = try? container.decode(boolType)
case let intType as Int.Type:
object = try? container.decode(intType)
case let int8Type as Int8.Type:
object = try? container.decode(int8Type)
case let int32Type as Int32.Type:
object = try? container.decode(int32Type)
case let int64Type as Int64.Type:
object = try? container.decode(int64Type)
case let uintType as UInt.Type:
object = try? container.decode(uintType)
case let uint8Type as UInt8.Type:
object = try? container.decode(uint8Type)
case let uint16Type as UInt16.Type:
object = try? container.decode(uint16Type)
case let uint32Type as UInt32.Type:
object = try? container.decode(uint32Type)
case let uint64Type as UInt64.Type:
object = try? container.decode(uint64Type)
case let doubleType as Double.Type:
object = try? container.decode(doubleType)
case let stringType as String.Type:
object = try? container.decode(stringType)
case let jsonValueArrayType as [JSON].Type:
object = try? container.decode(jsonValueArrayType)
case let jsonValueDictType as [String: JSON].Type:
object = try? container.decode(jsonValueDictType)
default:
break
}
}
}
self.init(object ?? NSNull())
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if object is NSNull {
try container.encodeNil()
return
}
switch object {
case let intValue as Int:
try container.encode(intValue)
case let int8Value as Int8:
try container.encode(int8Value)
case let int32Value as Int32:
try container.encode(int32Value)
case let int64Value as Int64:
try container.encode(int64Value)
case let uintValue as UInt:
try container.encode(uintValue)
case let uint8Value as UInt8:
try container.encode(uint8Value)
case let uint16Value as UInt16:
try container.encode(uint16Value)
case let uint32Value as UInt32:
try container.encode(uint32Value)
case let uint64Value as UInt64:
try container.encode(uint64Value)
case let doubleValue as Double:
try container.encode(doubleValue)
case let boolValue as Bool:
try container.encode(boolValue)
case let stringValue as String:
try container.encode(stringValue)
case is [Any]:
let jsonValueArray = array ?? []
try container.encode(jsonValueArray)
case is [String: Any]:
let jsonValueDictValue = dictionary ?? [:]
try container.encode(jsonValueDictValue)
default:
break
}
}
}
8 changes: 8 additions & 0 deletions SwiftyJSON.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
5DD502911D9B21810004C112 /* NestedJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DD502901D9B21810004C112 /* NestedJSONTests.swift */; };
5DD502921D9B21810004C112 /* NestedJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DD502901D9B21810004C112 /* NestedJSONTests.swift */; };
5DD502931D9B21810004C112 /* NestedJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DD502901D9B21810004C112 /* NestedJSONTests.swift */; };
712921EF2004E4EB00DA6340 /* CodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 712921EE2004E4EB00DA6340 /* CodableTests.swift */; };
712921F02004E4EB00DA6340 /* CodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 712921EE2004E4EB00DA6340 /* CodableTests.swift */; };
712921F12004E4EB00DA6340 /* CodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 712921EE2004E4EB00DA6340 /* CodableTests.swift */; };
7236B4EE1BAC14150020529B /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8491E1D19CD6DAE00CCFAE6 /* SwiftyJSON.swift */; };
7236B4F11BAC14150020529B /* SwiftyJSON.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E4FEFE019575BE100351305 /* SwiftyJSON.h */; settings = {ATTRIBUTES = (Public, ); }; };
9C459EF41A910334008C9A41 /* SwiftyJSON.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E4FEFE019575BE100351305 /* SwiftyJSON.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -117,6 +120,7 @@
2E4FEFE019575BE100351305 /* SwiftyJSON.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftyJSON.h; sourceTree = "<group>"; };
2E4FEFE619575BE100351305 /* SwiftyJSON iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SwiftyJSON iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
5DD502901D9B21810004C112 /* NestedJSONTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NestedJSONTests.swift; sourceTree = "<group>"; };
712921EE2004E4EB00DA6340 /* CodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableTests.swift; sourceTree = "<group>"; };
7236B4F61BAC14150020529B /* SwiftyJSON.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftyJSON.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7236B4F71BAC14150020529B /* Info-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-tvOS.plist"; sourceTree = "<group>"; };
9C459EF61A9103B1008C9A41 /* Info-macOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-macOS.plist"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -265,6 +269,7 @@
A8B66C8B19E51D6500540692 /* DictionaryTests.swift */,
A830A6941E5B2DD8001D7F6D /* MutabilityTests.swift */,
A8B66C8D19E52F4200540692 /* ArrayTests.swift */,
712921EE2004E4EB00DA6340 /* CodableTests.swift */,
2E4FEFEB19575BE100351305 /* Supporting Files */,
);
name = SwiftyJSONTests;
Expand Down Expand Up @@ -604,6 +609,7 @@
A819C49F19E2EE5B00ADCC3D /* SubscriptTests.swift in Sources */,
A830A6951E5B2DD8001D7F6D /* MutabilityTests.swift in Sources */,
A863BE2819EED46F0092A41F /* RawTests.swift in Sources */,
712921EF2004E4EB00DA6340 /* CodableTests.swift in Sources */,
A885D1D219CF1EE6002FD4C3 /* BaseTests.swift in Sources */,
A8B66C8E19E52F4200540692 /* ArrayTests.swift in Sources */,
A8B66C8C19E51D6500540692 /* DictionaryTests.swift in Sources */,
Expand Down Expand Up @@ -643,6 +649,7 @@
9C459EFA1A9103C1008C9A41 /* BaseTests.swift in Sources */,
A830A6961E5B2DD8001D7F6D /* MutabilityTests.swift in Sources */,
9C459F041A9103C1008C9A41 /* DictionaryTests.swift in Sources */,
712921F02004E4EB00DA6340 /* CodableTests.swift in Sources */,
9C459EF91A9103C1008C9A41 /* PerformanceTests.swift in Sources */,
9C459EFE1A9103C1008C9A41 /* LiteralConvertibleTests.swift in Sources */,
9C459EFC1A9103C1008C9A41 /* PrintableTests.swift in Sources */,
Expand All @@ -666,6 +673,7 @@
A8580F841BCF69A000DA927B /* SubscriptTests.swift in Sources */,
A830A6971E5B2DD8001D7F6D /* MutabilityTests.swift in Sources */,
A8580F851BCF69A000DA927B /* LiteralConvertibleTests.swift in Sources */,
712921F12004E4EB00DA6340 /* CodableTests.swift in Sources */,
A8580F861BCF69A000DA927B /* RawRepresentableTests.swift in Sources */,
A8580F871BCF69A000DA927B /* ComparableTests.swift in Sources */,
A8580F881BCF69A000DA927B /* StringTests.swift in Sources */,
Expand Down
100 changes: 100 additions & 0 deletions Tests/SwiftyJSONTests/CodableTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// CodableTests.swift
//
// Created by Lei Wang on 2018/1/9.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import XCTest
import SwiftyJSON

class CodableTests: XCTestCase {

func testEncodeNull() {
var json = JSON([NSNull()])
_ = try! JSONEncoder().encode(json)
json = JSON([nil])
_ = try! JSONEncoder().encode(json)
let dictionary: [String: Any?] = ["key": nil]
json = JSON(dictionary)
_ = try! JSONEncoder().encode(json)
}
func testArrayCodable() {
let jsonString = """
[1,"false", ["A", 4.3231],"3",true]
"""
var data = jsonString.data(using: .utf8)!
let json = try! JSONDecoder().decode(JSON.self, from: data)
XCTAssertEqual(json.arrayValue.first?.int, 1)
XCTAssertEqual(json[1].bool, nil)
XCTAssertEqual(json[1].string, "false")
XCTAssertEqual(json[3].string, "3")
XCTAssertEqual(json[2][1].double!, 4.3231)
XCTAssertEqual(json.arrayValue[0].bool, nil)
XCTAssertEqual(json.array!.last!.bool, true)
let jsonList = try! JSONDecoder().decode([JSON].self, from: data)
XCTAssertEqual(jsonList.first?.int, 1)
XCTAssertEqual(jsonList.last!.bool, true)
data = try! JSONEncoder().encode(json)
let list = try! JSONSerialization.jsonObject(with: data, options: []) as! [Any]
XCTAssertEqual(list[0] as! Int, 1)
XCTAssertEqual((list[2] as! [Any])[1] as! Float, 4.3231)
}
func testDictionaryCodable() {
let dictionary: [String: Any] = ["number": 9823.212, "name": "NAME", "list": [1234, 4.21223256], "object": ["sub_number": 877.2323, "sub_name": "sub_name"], "bool": true]
var data = try! JSONSerialization.data(withJSONObject: dictionary, options: [])
let json = try! JSONDecoder().decode(JSON.self, from: data)
XCTAssertNotNil(json.dictionary)
XCTAssertEqual(json["number"].float, 9823.212)
XCTAssertEqual(json["list"].arrayObject is [Float], true)
XCTAssertEqual(json["object"]["sub_number"].float, 877.2323)
XCTAssertEqual(json["bool"].bool, true)
let jsonDict = try! JSONDecoder().decode([String: JSON].self, from: data)
XCTAssertEqual(jsonDict["number"]?.int, 9823)
XCTAssertEqual(jsonDict["object"]?["sub_name"], "sub_name")
data = try! JSONEncoder().encode(json)
var encoderDict = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
XCTAssertEqual(encoderDict["list"] as! [Float], [1234, 4.21223256])
XCTAssertEqual(encoderDict["bool"] as! Bool, true)
data = try! JSONEncoder().encode(jsonDict)
encoderDict = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
XCTAssertEqual(encoderDict["name"] as! String, dictionary["name"] as! String)
XCTAssertEqual((encoderDict["object"] as! [String: Any])["sub_number"] as! Float, 877.2323)
}
func testCodableModel() {
struct CodableModel: Codable {
let name: String
let number: Double
let bool: Bool
let list: [Double]
private let object: JSON
var subName: String? {
return object["sub_name"].string
}
}
let dictionary: [String: Any] = [
"number": 9823.212,
"name": "NAME",
"list": [1234, 4.21223256],
"object": ["sub_number": 877.2323, "sub_name": "sub_name"],
"bool": true]
let data = try! JSONSerialization.data(withJSONObject: dictionary, options: [])
let model = try! JSONDecoder().decode(CodableModel.self, from: data)
XCTAssertEqual(model.subName, "sub_name")
}
}

0 comments on commit 38d9815

Please sign in to comment.