Swift Language
Чтение и письмо JSON
Поиск…
Синтаксис
- NSJSONSerialization.JSONObjectWithData (jsonData, options: NSJSONReadingOptions) // Возвращает объект из jsonData. Этот метод бросает неудачу.
- NSJSONSerialization.dataWithJSONObject (jsonObject, options: NSJSONWritingOptions) // Возвращает NSData из объекта JSON. Перейдите в NSJSONWritingOptions.PrettyPrinted в опции для вывода, который более читабельен.
Сериализация JSON, кодирование и декодирование с помощью Apple Foundation и стандартной библиотеки Swift
Класс JSONSerialization встроен в структуру Apple Foundation.
Читать JSON
Функция JSONObjectWithData
принимает NSData
и возвращает AnyObject
. Вы можете использовать as?
для преобразования результата в ожидаемый тип.
do {
guard let jsonData = "[\"Hello\", \"JSON\"]".dataUsingEncoding(NSUTF8StringEncoding) else {
fatalError("couldn't encode string as UTF-8")
}
// Convert JSON from NSData to AnyObject
let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
// Try to convert AnyObject to array of strings
if let stringArray = jsonObject as? [String] {
print("Got array of strings: \(stringArray.joinWithSeparator(", "))")
}
} catch {
print("error reading JSON: \(error)")
}
Вы можете передавать options: .AllowFragments
вместо options: []
чтобы разрешить чтение JSON, когда объект верхнего уровня не является массивом или словарем.
Написать JSON
Вызов dataWithJSONObject
преобразует JSON-совместимый объект (вложенные массивы или словари со строками, номерами и NSNull
) в необработанные NSData
закодированные как UTF-8.
do {
// Convert object to JSON as NSData
let jsonData = try NSJSONSerialization.dataWithJSONObject(jsonObject, options: [])
print("JSON data: \(jsonData)")
// Convert NSData to String
let jsonString = String(data: jsonData, encoding: NSUTF8StringEncoding)!
print("JSON string: \(jsonString)")
} catch {
print("error writing JSON: \(error)")
}
Вы можете передать options: .PrettyPrinted
вместо options: []
для довольно-печатной.
Такое же поведение в Swift 3, но с другим синтаксисом.
do {
guard let jsonData = "[\"Hello\", \"JSON\"]".data(using: String.Encoding.utf8) else {
fatalError("couldn't encode string as UTF-8")
}
// Convert JSON from NSData to AnyObject
let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: [])
// Try to convert AnyObject to array of strings
if let stringArray = jsonObject as? [String] {
print("Got array of strings: \(stringArray.joined(separator: ", "))")
}
} catch {
print("error reading JSON: \(error)")
}
do {
// Convert object to JSON as NSData
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: [])
print("JSON data: \(jsonData)")
// Convert NSData to String
let jsonString = String(data: jsonData, encoding: .utf8)!
print("JSON string: \(jsonString)")
} catch {
print("error writing JSON: \(error)")
}
Примечание. В настоящее время следующие доступны только в Swift 4.0 и более поздних версиях.
Начиная с Swift 4.0, стандартная библиотека Swift включает в себя протоколы Encodable
и Decodable
для определения стандартизованного подхода к кодированию и декодированию данных. Принятие этих протоколов позволит реализации протоколов Encoder
и Decoder
принимать ваши данные и кодировать или декодировать их в и из внешнего представления, такого как JSON. Соответствие с Codable
протоколом сочетает в Encodable
и Decodable
протоколы. Это теперь рекомендуемое средство для обработки JSON в вашей программе.
Автоматическое кодирование и декодирование
Самый простой способ сделать тип codable - объявить его свойства как типы, которые уже являются Codable
. Эти типы включают стандартные типы библиотек, такие как String
, Int
и Double
; и типы Foundation, такие как Date
, Data
и URL
. Если свойства типа можно кодировать, сам тип автоматически будет соответствовать Codable
простым объявлением соответствия.
Рассмотрим следующий пример, в котором структура Book
соответствует Codable
.
struct Book: Codable {
let title: String
let authors: [String]
let publicationDate: Date
}
Обратите внимание, что стандартные коллекции, такие как
Array
иDictionary
соответствуютCodable
если они содержат типы codable.
Приняв Codable
, структура Book
теперь может быть закодирована и декодирована из JSON с использованием классов Apple Foundation JSONEncoder
и JSONDecoder
, хотя сама Book
не содержит кода для специфической обработки JSON. Пользовательские кодировщики и декодеры также могут быть записаны, соответственно, в соответствии с протоколами Encoder
и Decoder
.
Кодировать данные JSON
// Create an instance of Book called book
let encoder = JSONEncoder()
let data = try! encoder.encode(book) // Do not use try! in production code
print(data)
Установите
encoder.outputFormatting = .prettyPrinted
для упрощения чтения. ## Декодирование данных JSON
Декодирование данных JSON
// Retrieve JSON string from some source
let jsonData = jsonString.data(encoding: .utf8)!
let decoder = JSONDecoder()
let book = try! decoder.decode(Book.self, for: jsonData) // Do not use try! in production code
print(book)
В приведенном выше примере
Book.self
сообщает декодеру того типа, который должен быть декодирован JSON.
Кодирование или декодирование Исключительно
Иногда вам могут не потребоваться кодирование и декодирование данных, например, когда вам нужно только считывать данные JSON из API или если ваша программа только передает данные JSON в API.
Если вы собираетесь писать только данные JSON, совместите свой тип с Encodable
.
struct Book: Encodable {
let title: String
let authors: [String]
let publicationDate: Date
}
Если вы собираетесь читать только данные JSON, совместите свой тип с Decodable
.
struct Book: Decodable {
let title: String
let authors: [String]
let publicationDate: Date
}
Использование пользовательских имен ключей
API часто используют соглашения об именах, отличные от случая верблюжьего верблюда, такого как случай с змеей. Это может стать проблемой, когда дело доходит до декодирования JSON, поскольку по умолчанию ключи JSON должны точно совпадать с именами свойств вашего типа. Чтобы справиться с этими сценариями, вы можете создавать собственные ключи для своего типа, используя протокол CodingKey
.
struct Book: Codable {
// ...
enum CodingKeys: String, CodingKey {
case title
case authors
case publicationDate = "publication_date"
}
}
CodingKeys
генерируются автоматически для типов , которые принимают в Codable
протокола, но путем создания нашей собственной реализации в приведенном выше примере , мы позволяем нашим декодером , чтобы соответствовать местному верблюд случай publicationDate
с змея случае publication_date
, как они поступают по API.
SwiftyJSON
SwiftyJSON - это структура Swift, созданная для устранения необходимости дополнительной привязки в обычной сериализации JSON.
Вы можете скачать его здесь: https://github.com/SwiftyJSON/SwiftyJSON
Без SwiftyJSON ваш код будет выглядеть так, чтобы найти имя первой книги в объекте JSON:
if let jsonObject = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [[String: AnyObject]],
let bookName = (jsonObject[0]["book"] as? [String: AnyObject])?["name"] as? String {
//We can now use the book name
}
В SwiftyJSON это очень упрощено:
let json = JSON(data: data)
if let bookName = json[0]["book"]["name"].string {
//We can now use the book name
}
Это устраняет необходимость проверять каждое поле, так как оно возвращает нуль, если какой-либо из них недействителен.
Чтобы использовать SwiftyJSON, загрузите правильную версию из репозитория Git - есть ветка для Swift 3. Просто перетащите «SwiftyJSON.swift» в свой проект и импортируйте в свой класс:
import SwiftyJSON
Вы можете создать свой объект JSON, используя следующие два инициализатора:
let jsonObject = JSON(data: dataObject)
или же
let jsonObject = JSON(jsonObject) //This could be a string in a JSON format for example
Чтобы получить доступ к своим данным, используйте индексы:
let firstObjectInAnArray = jsonObject[0]
let nameOfFirstObject = jsonObject[0]["name"]
Затем вы можете проанализировать свое значение на определенный тип данных, который вернет необязательное значение:
let nameOfFirstObject = jsonObject[0]["name"].string //This will return the name as a string
let nameOfFirstObject = jsonObject[0]["name"].double //This will return null
Вы также можете скомпилировать свои пути в быстрый массив:
let convolutedPath = jsonObject[0]["name"][2]["lastName"]["firstLetter"].string
Такой же как:
let convolutedPath = jsonObject[0, "name", 2, "lastName", "firstLetter"].string
SwiftyJSON также имеет функциональность для печати собственных ошибок:
if let name = json[1337].string {
//You can use the value - it is valid
} else {
print(json[1337].error) // "Array[1337] is out of bounds" - You cant use the value
}
Если вам нужно написать свой объект JSON, вы можете снова использовать индексы:
var originalJSON:JSON = ["name": "Jack", "age": 18]
originalJSON["age"] = 25 //This changes the age to 25
originalJSON["surname"] = "Smith" //This creates a new field called "surname" and adds the value to it
Если вам нужна оригинальная строка для JSON, например, если вам нужно записать ее в файл, вы можете получить исходное значение:
if let string = json.rawString() { //This is a String object
//Write the string to a file if you like
}
if let data = json.rawData() { //This is an NSData object
//Send the data to your server if you like
}
Фредди
Freddy - это библиотека разбора JSON, которую ведет Big Nerd Ranch . Он имеет три основных преимущества:
Тип Безопасность: Помогает вам работать с отправкой и получением JSON таким образом, чтобы предотвратить сбои во время выполнения.
Idiomatic: использует преимущества генераторов Swift, перечислений и функциональных возможностей без сложной документации или магических пользовательских операторов.
Обработка ошибок. Предоставляет информативную информацию об ошибках для часто встречающихся ошибок JSON.
Пример данных JSON
Давайте определим некоторые примеры данных JSON для использования с этими примерами.
{ "success": true, "people": [ { "name": "Matt Mathias", "age": 32, "spouse": true }, { "name": "Sergeant Pepper", "age": 25, "spouse": false } ], "jobs": [ "teacher", "judge" ], "states": { "Georgia": [ 30301, 30302, 30303 ], "Wisconsin": [ 53000, 53001 ] } }
let jsonString = "{\"success\": true, \"people\": [{\"name\": \"Matt Mathias\",\"age\": 32,\"spouse\": true},{\"name\": \"Sergeant Pepper\",\"age\": 25,\"spouse\": false}],\"jobs\": [\"teacher\",\"judge\"],\"states\": {\"Georgia\": [30301,30302,30303],\"Wisconsin\": [53000,53001]}}" let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)!
Удаление десериализации исходных данных
Чтобы десериализовать данные, мы инициализируем объект JSON
затем обращаемся к определенному ключу.
do { let json = try JSON(data: jsonData) let success = try json.bool("success") } catch { // do something with the error }
Мы try
здесь, потому что доступ к json
для ключевого "success"
может быть неудачным - он может не существовать, или значение может быть не логическим.
Мы также можем указать путь доступа к элементам, вложенным в структуру JSON. Путь представляет собой список ключей и индексов, разделенных запятыми, которые описывают путь к интересующей стоимости.
do { let json = try JSON(data: jsonData) let georgiaZipCodes = try json.array("states", "Georgia") let firstPersonName = try json.string("people", 0, "name") } catch { // do something with the error }
Десериализация моделей напрямую
JSON может быть непосредственно разобран на класс модели, который реализует протокол JSONDecodable
.
public struct Person { public let name: String public let age: Int public let spouse: Bool } extension Person: JSONDecodable { public init(json: JSON) throws { name = try json.string("name") age = try json.int("age") spouse = try json.bool("spouse") } } do { let json = try JSON(data: jsonData) let people = try json.arrayOf("people", type: Person.self) } catch { // do something with the error }
Сериализация исходных данных
Любое значение JSON
может быть сериализовано непосредственно в NSData
.
let success = JSON.Bool(false) let data: NSData = try success.serialize()
Сериализация моделей напрямую
Любой класс модели, реализующий протокол JSONEncodable
, может быть сериализован непосредственно в NSData
.
extension Person: JSONEncodable { public func toJSON() -> JSON { return .Dictionary([ "name": .String(name), "age": .Int(age), "spouse": .Bool(spouse) ]) } } let newPerson = Person(name: "Glenn", age: 23, spouse: true) let data: NSData = try newPerson.toJSON().serialize()
Стрела
Arrow - это элегантная библиотека разбора JSON в Swift.
Он позволяет анализировать JSON и сопоставлять его с пользовательскими классами моделей с помощью оператора <--
:
identifier <-- json["id"]
name <-- json["name"]
stats <-- json["stats"]
Пример:
Модель Swift
struct Profile {
var identifier = 0
var name = ""
var link: NSURL?
var weekday: WeekDay = .Monday
var stats = Stats()
var phoneNumbers = [PhoneNumber]()
}
Файл JSON
{
"id": 15678,
"name": "John Doe",
"link": "https://apple.com/steve",
"weekdayInt" : 3,
"stats": {
"numberOfFriends": 163,
"numberOfFans": 10987
},
"phoneNumbers": [{
"label": "house",
"number": "9809876545"
}, {
"label": "cell",
"number": "0908070656"
}, {
"label": "work",
"number": "0916570656"
}]
}
картографирование
extension Profile: ArrowParsable {
mutating func deserialize(json: JSON) {
identifier <-- json["id"]
link <-- json["link"]
name <-- json["name"]
weekday <-- json["weekdayInt"]
stats <- json["stats"]
phoneNumbers <-- json["phoneNumbers"]
}
}
использование
let profile = Profile()
profile.deserialize(json)
Монтаж:
Карфаген
github "s4cha/Arrow"
CocoaPods
pod 'Arrow'
use_frameworks!
Вручную
Просто скопируйте и вставьте Arrow.swift в свой проект Xcode
https://github.com/s4cha/Arrow
Как A Framework
Загрузите Arrow из репозитория GitHub и создайте целевой объект Framework в примере проекта. Затем ссылку на эту структуру.
Простой JSON-анализ в пользовательских объектах
Даже если сторонние библиотеки хороши, простой способ разобрать JSON обеспечивается протоколами. Вы можете себе представить, что у вас есть объект Todo
as
struct Todo {
let comment: String
}
Всякий раз, когда вы получаете JSON, вы можете обрабатывать простые NSData
как показано в другом примере, используя объект NSJSONSerialization
.
После этого, используя простой протокол JSONDecodable
typealias JSONDictionary = [String:AnyObject]
protocol JSONDecodable {
associatedtype Element
static func from(json json: JSONDictionary) -> Element?
}
И превращение вашей структуры Todo
соответствие с JSONDecodable
делает трюк
extension Todo: JSONDecodable {
static func from(json json: JSONDictionary) -> Todo? {
guard let comment = json["comment"] as? String else { return nil }
return Todo(comment: comment)
}
}
Вы можете попробовать это с помощью json-кода:
{
"todos": [
{
"comment" : "The todo comment"
}
]
}
Когда вы получили его из API, вы можете сериализовать его как предыдущие примеры, показанные в экземпляре AnyObject
. После этого вы можете проверить, является ли экземпляр экземпляром JSONDictionary
guard let jsonDictionary = dictionary as? JSONDictionary else { return }
Другое дело, чтобы проверить, конкретный для этого случая, потому что у вас есть массив Todo
в JSON, это словарь todos
guard let todosDictionary = jsonDictionary["todos"] as? [JSONDictionary] else { return }
Теперь, когда вы получили массив словарей, вы можете преобразовать каждый из них в объект Todo
с помощью flatMap
(он автоматически удалит значения nil
из массива)
let todos: [Todo] = todosDictionary.flatMap { Todo.from(json: $0) }
JSON Parsing Swift 3
Вот файл JSON, который мы будем использовать под названием animals.json
{
"Sea Animals": [
{
"name": "Fish",
"question": "How many species of fish are there?" },
{
"name": "Sharks",
"question": "How long do sharks live?"
},
{
"name": "Squid",
"question": "Do squids have brains?"
},
{
"name": "Octopus",
"question": "How big do octopus get?"
},
{
"name": "Star Fish",
"question": "How long do star fish live?"
}
],
"mammals": [
{
"name": "Dog",
"question": "How long do dogs live?"
},
{
"name": "Elephant",
"question": "How much do baby elephants weigh?"
},
{
"name": "Cats",
"question": "Do cats really have 9 lives?"
},
{
"name": "Tigers",
"question": "Where do tigers live?"
},
{
"name": "Pandas",
"question": "WHat do pandas eat?"
} ] }
Импортируйте свой JSON-файл в свой проект
Вы можете выполнить эту простую функцию, чтобы распечатать свой файл JSON
func jsonParsingMethod() {
//get the file
let filePath = Bundle.main.path(forResource: "animals", ofType: "json")
let content = try! String(contentsOfFile: filePath!)
let data: Data = content.data(using: String.Encoding.utf8)!
let json: NSDictionary = try! JSONSerialization.jsonObject(with: data as Data, options:.mutableContainers) as! NSDictionary
//Call which part of the file you'd like to pare
if let results = json["mammals"] as? [[String: AnyObject]] {
for res in results {
//this will print out the names of the mammals from out file.
if let rates = res["name"] as? String {
print(rates)
}
}
}
}
Если вы хотите поместить его в табличное представление, я бы сначала создал словарь с NSObject.
Создайте новый быстрый файл под названием ParsingObject
и создайте строковые переменные.
Убедитесь, что имя переменной совпадает с именем файла JSON
, Например, в нашем проекте у нас есть name
и question
поэтому в нашем новом быстром файле мы будем использовать
var name: String?
var question: String?
Инициализируйте NSObject, который мы вернули в наш ViewController.swift var array = ParsingObject. Затем мы выполнили тот же самый метод, который мы имели раньше, с незначительной модификацией.
func jsonParsingMethod() {
//get the file
let filePath = Bundle.main.path(forResource: "animals", ofType: "json")
let content = try! String(contentsOfFile: filePath!)
let data: Data = content.data(using: String.Encoding.utf8)!
let json: NSDictionary = try! JSONSerialization.jsonObject(with: data as Data, options:.mutableContainers) as! NSDictionary
//This time let's get Sea Animals
let results = json["Sea Animals"] as? [[String: AnyObject]]
//Get all the stuff using a for-loop
for i in 0 ..< results!.count {
//get the value
let dict = results?[i]
let resultsArray = ParsingObject()
//append the value to our NSObject file
resultsArray.setValuesForKeys(dict!)
array.append(resultsArray)
}
}
Затем мы показываем это в нашем столе, делая это,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//This is where our values are stored
let object = array[indexPath.row]
cell.textLabel?.text = object.name
cell.detailTextLabel?.text = object.question
return cell
}