Skip to content

Instantly share code, notes, and snippets.

@mikeash
Created June 16, 2014 22:30
Show Gist options
  • Save mikeash/dc4d85367b742124c5c9 to your computer and use it in GitHub Desktop.
Save mikeash/dc4d85367b742124c5c9 to your computer and use it in GitHub Desktop.
#!/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