Created
June 16, 2014 22:30
-
-
Save mikeash/dc4d85367b742124c5c9 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
#!/Applications/Xcode6-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -i -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk | |
import Foundation | |
class JSON { | |
struct Path: Printable { | |
enum Element { | |
case Index(Int) | |
case Key(String) | |
var asString: String { | |
get { | |
switch self { | |
case .Index(let value): | |
return String(value) | |
case .Key(let value): | |
return value | |
} | |
} | |
} | |
} | |
let elements: Element[] | |
init(elements: Element[]) { | |
self.elements = elements | |
} | |
init() { | |
self.init(elements: Element[]()) | |
} | |
var description: String { | |
if elements.count == 0 { | |
return "<empty>" | |
} | |
let strings = elements.map { $0.asString } | |
return ".".join(strings) | |
} | |
} | |
struct Error: Printable { | |
let path: Path | |
let desiredType: String | |
let actualType: String | |
var description: String { | |
return "At \(path.description), got \(actualType) but wanted \(desiredType)" | |
} | |
} | |
struct Value { | |
let errorReport: Error -> Void | |
let underlyingObject: NSObject? | |
func err(desiredType: String) -> Void { | |
let path = Path() | |
var actualType = "nil" | |
if let obj = underlyingObject { | |
actualType = NSStringFromClass(obj.classForCoder()) | |
} | |
errorReport(Error(path: path, desiredType: desiredType, actualType: actualType)) | |
} | |
var string: String? { | |
if let underlyingString = underlyingObject as? NSString { | |
return underlyingString | |
} | |
err("string") | |
return nil | |
} | |
var number: Double? { | |
if let underlyingNumber = underlyingObject as? NSNumber { | |
return underlyingNumber.doubleValue() | |
} | |
err("number") | |
return nil | |
} | |
var dictionary: Dictionary? { | |
if let underlyingDictionary = underlyingObject as? NSDictionary { | |
return Dictionary(underlyingDictionary, errorReport) | |
} | |
err("dictionary") | |
return nil | |
} | |
var array: Array? { | |
if let underlyingArray = underlyingObject as? NSArray { | |
return Array(underlyingArray, errorReport) | |
} | |
err("array") | |
return nil | |
} | |
} | |
struct Dictionary { | |
let errorReport: Error -> Void | |
let underlyingDictionary: NSDictionary | |
init(_ underlyingDictionary: NSDictionary, _ errorReport: Error -> Void) { | |
self.underlyingDictionary = underlyingDictionary | |
self.errorReport = errorReport | |
} | |
subscript(key: String) -> Value { | |
return Value( | |
errorReport: { | |
(error: Error) in | |
let newError = Path.Element.Key(key) + error | |
self.errorReport(newError) | |
}, | |
underlyingObject: underlyingDictionary[key] as? NSObject | |
) | |
} | |
} | |
struct Array { | |
let errorReport: Error -> Void | |
let underlyingArray: NSArray | |
init(_ underlyingArray: NSArray, _ errorReport: Error -> Void) { | |
self.underlyingArray = underlyingArray | |
self.errorReport = errorReport | |
} | |
subscript(index: Int) -> Value { | |
return Value( | |
errorReport: { | |
(error: Error) in | |
let newError = Path.Element.Index(index) + error | |
self.errorReport(newError) | |
}, | |
underlyingObject: underlyingArray[index] as? NSObject | |
) | |
} | |
} | |
class func extract<T>(obj: AnyObject?, block: (Value, (Void -> T?) -> Void) -> Void) -> (T?, Error[]?) { | |
var errors = Error[]() | |
let value = Value(errorReport: { errors.append($0) }, underlyingObject: obj as? NSObject) | |
var result: T? | |
block(value) { | |
thenBlock in | |
if errors.count == 0 { | |
result = thenBlock() | |
} | |
} | |
if errors.count == 0 { | |
return (result, nil) | |
} else { | |
return (nil, errors) | |
} | |
} | |
} | |
func + (left: JSON.Path.Element, right: JSON.Path) -> JSON.Path { | |
var newElements = right.elements | |
newElements.insert(left, atIndex: 0) | |
return JSON.Path(elements: newElements) | |
} | |
func + (left: JSON.Path.Element, right: JSON.Error) -> JSON.Error { | |
return JSON.Error(path: (left + right.path), desiredType: right.desiredType, actualType: right.actualType) | |
} | |
let jsonIn = [ | |
"a": "b", | |
"c": 2, | |
"d": [ 3, 4, 5], | |
"e": [ | |
"f" : "g" | |
] | |
] | |
let jsonData = NSJSONSerialization.dataWithJSONObject(jsonIn, options: NSJSONWritingOptions(0), error: nil) | |
let jsonOut : AnyObject! = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions(0), error: nil) | |
let (x: NSString?, errors: JSON.Error[]?) = JSON.extract(jsonOut) { value, then in | |
if let dict = value.dictionary { | |
let a = dict["a"].string | |
let c = dict["c"].number | |
let d0 = dict["d"].array?[0].number | |
let d1 = dict["d"].array?[1].number | |
let e = dict["e"].dictionary?["f"].string | |
then { | |
return "\(a) \(c) \(d0) \(d1) \(e)" | |
} | |
} | |
} | |
println(x) | |
if let realErrors = errors { | |
println(realErrors.map{ $0.description }) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment