This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
public class JSONObject { | |
public struct Error { | |
public enum Element: Equatable { | |
case Index(Int) | |
case Key(String) | |
} | |
public let keyPath: [Element] | |
public let expectedTypeName: String | |
public let actualTypeName: String | |
} | |
let underlyingValue: AnyObject? | |
public private(set) var errors = [Error]() | |
let keyPath: [Error.Element] | |
let parent: JSONObject? | |
public init(underlyingValue: AnyObject?, keyPath: [Error.Element], parent: JSONObject?) { | |
self.underlyingValue = underlyingValue | |
self.keyPath = keyPath | |
self.parent = parent | |
} | |
public convenience init(_ underlyingValue: AnyObject?) { | |
self.init(underlyingValue: underlyingValue, keyPath: [], parent: nil) | |
} | |
func get<T>(typename: String) -> T? { | |
if let typedValue = underlyingValue as? T { | |
return typedValue | |
} else { | |
let typeName = underlyingValue.map{ NSStringFromClass($0.classForCoder) } | |
let error = Error(keyPath: keyPath, expectedTypeName: typename, actualTypeName: typeName ?? "nil") | |
addError(error) | |
return nil | |
} | |
} | |
func addError(error: Error) { | |
errors.append(error) | |
parent?.addError(error) | |
} | |
public var string: String { | |
return get("string") ?? "" | |
} | |
public var double: Double { | |
return get("number") ?? 0.0 | |
} | |
public var dict: JSONDictionary { | |
if let dict: NSDictionary = get("dictionary") { | |
return JSONDictionary(parent: self, dict: dict) | |
} else { | |
return JSONDictionary(parent: nil, dict: [:]) | |
} | |
} | |
public var array: JSONArray { | |
if let array: NSArray = get("array") { | |
return JSONArray(parent: self, array: array) | |
} else { | |
return JSONArray(parent: nil, array: []) | |
} | |
} | |
} | |
public struct JSONDictionary { | |
let parent: JSONObject? | |
let dict: NSDictionary | |
public subscript(key: String) -> JSONObject { | |
let keyPath = (parent?.keyPath ?? []) + [.Key(key)] | |
return JSONObject(underlyingValue: dict[key], keyPath: keyPath, parent: parent) | |
} | |
} | |
public struct JSONArray { | |
let parent: JSONObject? | |
let array: NSArray | |
public subscript(key: Int) -> JSONObject { | |
let value: AnyObject? = key < array.count ? array[key] : nil | |
let keyPath = (parent?.keyPath ?? []) + [.Index(key)] | |
return JSONObject(underlyingValue: value, keyPath: keyPath, parent: parent) | |
} | |
public func map<T>(f: JSONObject -> T) -> [T] { | |
var results = [T]() | |
for i in 0 ..< array.count { | |
results.append(f(self[i])) | |
} | |
return results | |
} | |
} | |
public func ==(a: JSONObject.Error.Element, b: JSONObject.Error.Element) -> Bool { | |
switch (a, b) { | |
case let (.Index(aIndex), .Index(bIndex)): | |
return aIndex == bIndex | |
case let(.Key(aKey), .Key(bKey)): | |
return aKey == bKey | |
default: | |
return false | |
} | |
} | |
public func Decode<T>(json: AnyObject?, f: JSONObject -> T) -> (T?, [JSONObject.Error]?) { | |
let obj = JSONObject(json) | |
let result = f(obj) | |
if obj.errors.count == 0 { | |
return (result, nil) | |
} else { | |
return (nil, obj.errors) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment