Работа с JSON — слишком привычное и повседневное занятие, чтобы уделять ему много внимания.
Однако реализация некоторых вещей в Swift выглядит слишком сложной и вызывает зубную боль каждый раз, когда вы это видите.
Недавно, читая пост о SwiftyVK , я нашел там ссылку на статья об OptJSON , что значительно упрощает работу с JSON в Swift. И хотя описанный в статье подход действительно интересен, я не мог не чувствовать, что он все же слишком сложен.
Я попытался еще немного упростить библиотеку OptJSON и вот что у меня получилось:
Исходный код библиотеки можно посмотреть по адресу ссылка на гитхаб : Загрузив один файл OptJSON.swift, я добавил его в пустой, недавно созданный тестовый проект для Apple TV. Xcode пожаловался, что версия Swift в файле слишком старая, и предложил обновить код. Я не возражал.let obj = json?["workplan"]?["presets"]?[1]?["id"] as? Int
Фактически исправления коснулись только удаления символов решетки (#).
Я также включил в проект конфигурацию JSON, которую ранее использовал в другом проекте, и попытался написать тестовый код для извлечения некоторой ценности «старомодным способом»: if let data = NSData(contentsOfFile: NSBundle.mainBundle().
pathForResource("config", ofType: "json")!) {
do {
let obj = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject]
let a = obj?["workplan"] as? [String: AnyObject]
let b = a?["presets"] as? [AnyObject]
print(b)
let c = b?[1] as? [String: AnyObject]
print(c)
let d = c?["id"] as? Int
print(d)
} catch {
print("Error!")
}
}
Да, в Objective C действительно было намного проще.
Теперь попробуем использовать OptJSON для тех же целей: Если это долго if let data = NSData(contentsOfFile: NSBundle.mainBundle().
pathForResource("config", ofType: "json")!) {
do {
let obj = try NSJSONSerialization.JSONObjectWithData(data, options: [])
let v = JSON(obj)
let a = v?[key:"workplan"]
let b = a?[key:"presets"]
print(b)
let c = b?[index:1]
print(c)
let d = c?[key:"id"]
print(d)
} catch {
print("Error!")
}
}
Или вкратце: if let data = NSData(contentsOfFile: NSBundle.mainBundle().
pathForResource("config", ofType: "json")!) {
do {
let obj = try NSJSONSerialization.JSONObjectWithData(data, options: [])
let d = JSON(obj)?[key:"workplan"]?[key:"presets"]?[index:1]?[key:"id"]
print(d)
} catch {
print("Error!")
}
}
Уже неплохо! Но все же я не могу избавиться от ощущения, что все можно сделать проще.
И это возможно! Я зашёл в OptJSON.swift и в первую очередь меня удивил дизайн: public func JSON(object: AnyObject?) -> JSONValue? {
if let some: AnyObject = object {
switch some {
case let null as NSNull: return null
case let number as NSNumber: return number
case let string as NSString: return string
case let array as NSArray: return array
case let dict as NSDictionary: return dict
default: return nil
}
} else {
return nil
}
}
Не долго думая, я заменил его на public func JSONValue(object: AnyObject?) -> JSONValue? {
if let some = object as? JSONValue {
return some
} else {
return nil
}
}
А если нет разницы, зачем платить больше? Следующим шагом было удаление именованных параметров, которые так раздражали при вызове индекса: [key:"presets"]?[index:0]?
Сказано - сделано! Xcode пожаловался, что ему не нравится возвращаемый тип JSONValue? вместо ожидаемого индекса AnyObject?.
Ну и заодно оборачиваем возвращаемые значения в JSON().
Код выглядел примерно так: extension NSArray : JSONValue {
public subscript( key: String) -> JSONValue? { return nil }
public subscript( index: Int) -> JSONValue? { return index < count && index >= 0 ? self[index] : nil }
}
extension NSDictionary : JSONValue {
public subscript( key: String) -> JSONValue? { return self[key] }
public subscript( index: Int) -> JSONValue? { return nil }
}
После запуска проекта я понял, почему автор решил использовать именованные параметры, а именно выполнение функции ушло в глубокую рекурсию, пытаясь вызвать self[key].
Но, в конце концов, зачем использовать классы NSArray и NSDictionary для расширения и чужеродный objectForKeyedSubscript для извлечения объектов?! Ведь есть родные для этих классов методы objectForKey: и objectAtIndex:, мы их и используем: extension NSArray : JSONValue {
public subscript( key: String) -> JSONValue? { return nil }
public subscript( index: Int) -> JSONValue? { return index < count && index >= 0 ? self.objectAtIndex(index) as? JSONValue : nil }
}
extension NSDictionary : JSONValue {
public subscript( key: String) -> JSONValue? { return self.objectForKey(key) as? JSONValue }
public subscript( index: Int) -> JSONValue? { return nil }
}
А раз начался такой запой, то функция JSON() для обёртки объектов нам в принципе не нужна; простого, как будет достаточно? JSONValue. Заменим его внутренности на что-то более удобное, например загрузку JSON-объекта из строки, NSData или из содержимого NSURL, и заодно избавимся от необходимости использовать новомодный try-catch: public func JSON(object: AnyObject?, options: NSJSONReadingOptions = []) -> JSONValue? {
let data: NSData
if let aData = object as? NSData {
data = aData
} else if let string = object as? String, aData = string.dataUsingEncoding(NSUTF8StringEncoding) {
data = aData
} else if let url = object as? NSURL, aData = NSData(contentsOfURL: url) {
data = aData
} else {
return nil
}
if let json = try? NSJSONSerialization.JSONObjectWithData(data, options: options) {
return json as? JSONValue
}
return nil
}
После этих преобразований окончательный код принимает следующий вид: if let v = JSON(NSBundle.mainBundle().
URLForResource("config", withExtension: "json")) {
let a = v["workplan"]
let b = a?["presets"]
print(b)
let c = b?[1]
print(c)
let d = c?["id"]
print(d)
}
А если короче, то вообще: let json = JSON(NSBundle.mainBundle().
URLForResource("config", withExtension: "json"))
let obj = json?["workplan"]?["presets"]?[1]?["id"] as? Int
print(obj)
И никаких именованных параметров! Спасибо за внимание.
Посмотреть полный код измененного файла import Foundation
public protocol JSONValue: AnyObject {
subscript(key: String) -> JSONValue? { get }
subscript(index: Int) -> JSONValue? { get }
}
extension NSNull : JSONValue {
public subscript(key: String) -> JSONValue? { return nil }
public subscript(index: Int) -> JSONValue? { return nil }
}
extension NSNumber : JSONValue {
public subscript(key: String) -> JSONValue? { return nil }
public subscript(index: Int) -> JSONValue? { return nil }
}
extension NSString : JSONValue {
public subscript( key: String) -> JSONValue? { return nil }
public subscript( index: Int) -> JSONValue? { return nil }
}
extension NSArray : JSONValue {
public subscript( key: String) -> JSONValue? { return nil }
public subscript( index: Int) -> JSONValue? { return index < count && index >= 0 ? self.objectAtIndex(index) as? JSONValue : nil }
}
extension NSDictionary : JSONValue {
public subscript( key: String) -> JSONValue? { return self.objectForKey(key) as? JSONValue }
public subscript( index: Int) -> JSONValue? { return nil }
}
public func JSON(object: AnyObject?, options: NSJSONReadingOptions = []) -> JSONValue? {
let data: NSData
if let aData = object as? NSData {
data = aData
} else if let string = object as? String, aData = string.dataUsingEncoding(NSUTF8StringEncoding) {
data = aData
} else if let url = object as? NSURL, aData = NSData(contentsOfURL: url) {
data = aData
} else {
return nil
}
if let json = try? NSJSONSerialization.JSONObjectWithData(data, options: options) {
return json as? JSONValue
}
return nil
}
Теги: #swift 2.0 #json #NSJSONSerialization #Разработка iOS #Swift
-
Что Делать, Если Ваш Android Tv Не Работает
19 Oct, 24 -
Эссе О Хостинге В Свободной Форме
19 Oct, 24 -
Gearman И Php 5.4 (5.6): Проблемы И Решения
19 Oct, 24