Skip to content

Instantly share code, notes, and snippets.

@dorentus
Last active October 14, 2015 08:36
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save dorentus/1e2132edfb174028bf11 to your computer and use it in GitHub Desktop.
Save dorentus/1e2132edfb174028bf11 to your computer and use it in GitHub Desktop.
//
// JSON.swift
//
//
// Created by ZHANG Yi on 2015-9-1.
//
// The MIT License (MIT)
//
// Copyright (c) 2015 ZHANG Yi <zhangyi.cn@gmail.com>
//
// 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.
//
public enum JSON {
case String(Swift.String)
case IntegerNumber(Int)
case FloatNumber(Double)
case Boolean(Bool)
case Null
indirect case Array([JSON])
indirect case Object([Swift.String: JSON])
}
extension JSON {
public var value: Any? {
switch self {
case .String(let value): return value
case .IntegerNumber(let value): return value
case .FloatNumber(let value): return value
case .Boolean(let value): return value
case .Null: return nil
case .Array(let array):
return array.map { $0.value }
case .Object(let dict):
var dictValue: [Swift.String: Any] = [:]
for (k, v) in dict {
dictValue[k] = v.value
}
return dictValue
}
}
}
extension JSON {
public init() {
self = .Object([:])
}
}
extension JSON: CustomStringConvertible {
public var description: Swift.String {
switch self {
case .String(let value):
return "\"\(value)\""
case .IntegerNumber(let value):
return "\(value)"
case .FloatNumber(let value):
return "\(value)"
case .Boolean(let value):
return "\(value)"
case .Null:
return "null"
case .Array(let value):
return "[" + value.map { $0.description }.joinWithSeparator(", ") + "]"
case .Object(let value):
return "{" + value.map { "\(JSON.String($0).description): \($1.description)" }.joinWithSeparator(", ") + "}"
}
}
}
extension JSON {
public subscript(index: Int) -> JSON? {
switch self {
case .Array(let value):
return value[index]
default:
return nil
}
}
public subscript(key: Swift.String) -> JSON? {
switch self {
case .Object(let value):
return value[key]
default:
return nil
}
}
}
extension JSON: NilLiteralConvertible {
public init(nilLiteral: ()) {
self = .Null
}
}
extension JSON: StringLiteralConvertible {
public init(unicodeScalarLiteral value: StringLiteralType) {
self = .String(value)
}
public init(extendedGraphemeClusterLiteral value: StringLiteralType) {
self = .String(value)
}
public init(stringLiteral value: StringLiteralType) {
self = .String(value)
}
}
extension JSON: ArrayLiteralConvertible {
public init(arrayLiteral elements: JSON...) {
self = .Array(elements)
}
}
extension JSON: DictionaryLiteralConvertible {
public init(dictionaryLiteral elements: (Swift.String, JSON)...) {
var dict = [Key: Value]()
elements.forEach { (k, v) in
dict[k] = v
}
self = .Object(dict)
}
}
extension JSON: FloatLiteralConvertible {
public init(floatLiteral value: Double) {
self = .FloatNumber(value)
}
}
extension JSON: IntegerLiteralConvertible {
public init(integerLiteral value: Int) {
self = .IntegerNumber(value)
}
}
extension JSON: BooleanLiteralConvertible {
public init(booleanLiteral value: Bool) {
self = .Boolean(value)
}
}
import Foundation
public protocol JSONConvertible {
func asJSON() -> JSON
}
extension JSON {
public struct SerializationFailed: ErrorType {
let on: AnyObject
}
public init(_ object: AnyObject) throws {
switch object {
case let string as Swift.String:
self = .String(string)
case let number as NSNumber:
switch CFNumberGetType(number) {
case .FloatType: fallthrough
case .Float32Type: fallthrough
case .Float64Type: fallthrough
case .CGFloatType: fallthrough
case .DoubleType:
self = .FloatNumber(number.doubleValue)
case .CharType:
self = .Boolean(number.boolValue)
default:
self = .IntegerNumber(number.integerValue)
}
case let arrayValue as [AnyObject]:
self = .Array(try arrayValue.map { try JSON($0) })
case let dictValue as [Swift.String: AnyObject]:
var dict: [Swift.String: JSON] = [:]
for (k, v) in zip(dictValue.keys, try dictValue.values.map { try JSON($0) }) {
dict[k] = v
}
self = .Object(dict)
case is NSNull:
self = .Null
case let v as JSONConvertible:
self = v.asJSON()
default:
throw SerializationFailed(on: object)
}
}
}
extension NSJSONSerialization {
public class func JSONWithData(data: NSData, options: NSJSONReadingOptions) throws -> JSON {
let object = try self.JSONObjectWithData(data, options: options)
return try JSON(object)
}
}
@dorentus
Copy link
Author

extension NSDate: JSONConvertible {
    public func asJSON() -> JSON {
        return .FloatNumber(self.timeIntervalSince1970)
    }
}

let x: JSON = [
    "description": "the description of the test case",
    "schema": ["the schema that should" : "be validated against"],
    "tests": [true, 1, 2.0]
]
x.description

let object = [
    "description": "the description of the test case",
    "schema": ["the schema that should" : "be validated against"],
    "tests": [
        [
            "description": "a specific test of a valid instance",
            "data": "the instance",
            "valid": true
        ],
        [
            "description": "another specific test this time, invalid",
            "data": 15,
            "valid": false
        ],
        NSNull(),
        NSDate()
    ]
]

do {
    let str = try JSON(object).description

    let json = try NSJSONSerialization.JSONWithData(str.dataUsingEncoding(NSUTF8StringEncoding)!, options: .AllowFragments )

    json.description

    json["tests"]?[1]
}
catch {
    print(error)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment