Json В Swift 2.0 Без Анестезии

Работа с JSON — слишком привычное и повседневное занятие, чтобы уделять ему много внимания.

Однако реализация некоторых вещей в Swift выглядит слишком сложной и вызывает зубную боль каждый раз, когда вы это видите.

Недавно, читая пост о SwiftyVK , я нашел там ссылку на статья об OptJSON , что значительно упрощает работу с JSON в Swift. И хотя описанный в статье подход действительно интересен, я не мог не чувствовать, что он все же слишком сложен.

Я попытался еще немного упростить библиотеку OptJSON и вот что у меня получилось:

  
  
  
  
  
  
  
  
  
  
  
   

let obj = json?["workplan"]?["presets"]?[1]?["id"] as? Int

Исходный код библиотеки можно посмотреть по адресу ссылка на гитхаб : Загрузив один файл OptJSON.swift, я добавил его в пустой, недавно созданный тестовый проект для Apple TV. Xcode пожаловался, что версия Swift в файле слишком старая, и предложил обновить код. Я не возражал.

Фактически исправления коснулись только удаления символов решетки (#).

Я также включил в проект конфигурацию 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

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.