Skip to content

Instantly share code, notes, and snippets.

@akisute
Created January 1, 2015 13:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akisute/8cc0fc43a3ffae000018 to your computer and use it in GitHub Desktop.
Save akisute/8cc0fc43a3ffae000018 to your computer and use it in GitHub Desktop.
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