Swift Language
Czytanie i pisanie JSON
Szukaj…
Składnia
- NSJSONSerialization.JSONObjectWithData (jsonData, opcje: NSJSONReadingOptions) // Zwraca obiekt z jsonData. Ta metoda powoduje awarię.
- NSJSONSerialization.dataWithJSONObject (jsonObject, opcje: NSJSONWritingOptions) // Zwraca NSData z obiektu JSON. Przekaż w NSJSONWritingOptions.PrettyPrinted w opcjach dla bardziej czytelnego wyniku.
Serializacja, kodowanie i dekodowanie JSON w Apple Foundation i Swift Standard Library
Klasa JSONSerialization jest wbudowana w środowisko Apple Foundation.
Przeczytaj JSON
Funkcja JSONObjectWithData
pobiera NSData
i zwraca AnyObject
. Możesz użyć as?
przekonwertować wynik na oczekiwany typ.
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)")
}
Możesz przekazać options: .AllowFragments
zamiast options: []
aby umożliwić odczyt JSON, gdy obiekt najwyższego poziomu nie jest tablicą ani słownikiem.
Napisz JSON
Wywołanie dataWithJSONObject
konwertuje obiekt zgodny z JSON (zagnieżdżone tablice lub słowniki z ciągami, liczbami i NSNull
) na surowe NSData
zakodowane jako 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)")
}
Możesz przekazać options: .PrettyPrinted
zamiast options: []
do ładnego drukowania.
Takie samo zachowanie w Swift 3, ale z inną składnią.
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)")
}
Uwaga: Poniższe jest obecnie dostępne tylko w Swift 4.0 i nowszych.
Począwszy od Swift 4.0, standardowa biblioteka Swift zawiera protokoły Encodable
i Decodable
celu zdefiniowania standardowego podejścia do kodowania i dekodowania danych. Przyjmując te protokoły pozwoli implementacje Encoder
i Decoder
protokołów zabrać swoje dane i zakodować lub odkodować go i od reprezentacji zewnętrznej, takich jak JSON. Zgodność z protokołem Codable
łączy protokoły Encodable
i Decodable
. Jest to obecnie zalecany sposób obsługi JSON w twoim programie.
Automatyczne kodowanie i dekodowanie
Najłatwiejszym sposobem na Codable
typu jest zadeklarowanie jego właściwości jako typów, które są już Codable
. Te typy obejmują standardowe typy bibliotek, takie jak String
, Int
i Double
; oraz typy Fundacji, takie jak Date
, Data
i URL
. Jeśli właściwości typu są kodowalne, sam typ automatycznie dostosuje się do Codable
, po prostu deklarując zgodność.
Rozważ następujący przykład, w którym struktura Book
jest zgodna z Codable
.
struct Book: Codable {
let title: String
let authors: [String]
let publicationDate: Date
}
Zauważ, że standardowe kolekcje, takie jak
Array
iDictionary
są zgodne zCodable
jeśli zawierają typyCodable
.
Dzięki przyjęciu Codable
, struktura Book
może być teraz kodowana i dekodowana z JSON przy użyciu klas Apple Foundation JSONEncoder
i JSONDecoder
, mimo że sama Book
nie zawiera kodu do specyficznej obsługi JSON. Niestandardowe kodery i dekodery można również zapisywać, odpowiednio dostosowując się do protokołów Encoder
i Decoder
.
Zakoduj do danych 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)
Ustaw
encoder.outputFormatting = .prettyPrinted
dla łatwiejszego czytania. ## Dekoduj z danych JSON
Dekoduj z danych 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)
W powyższym przykładzie
Book.self
informuje dekoder o typie, do którego należy dekodować JSON.
Wyłącznie kodowanie lub dekodowanie
Czasami dane mogą nie być zarówno kodowalne, jak i dekodowalne, na przykład gdy potrzebujesz tylko odczytać dane JSON z interfejsu API lub jeśli twój program przesyła tylko dane JSON do interfejsu API.
Jeśli zamierzasz tylko zapisywać dane JSON, dostosuj swój typ do Encodable
.
struct Book: Encodable {
let title: String
let authors: [String]
let publicationDate: Date
}
Jeśli zamierzasz tylko czytać dane JSON, dostosuj swój typ do Decodable
.
struct Book: Decodable {
let title: String
let authors: [String]
let publicationDate: Date
}
Używanie niestandardowych nazw kluczy
Interfejsy API często stosują konwencje nazewnictwa inne niż standardowe etui na wielbłądy Swift, takie jak przypadek węża. Może to stanowić problem przy dekodowaniu JSON, ponieważ domyślnie klucze JSON muszą być dokładnie dopasowane do nazw właściwości twojego typu. Aby obsłużyć te scenariusze, możesz utworzyć niestandardowe klucze dla swojego typu za pomocą protokołu CodingKey
.
struct Book: Codable {
// ...
enum CodingKeys: String, CodingKey {
case title
case authors
case publicationDate = "publication_date"
}
}
CodingKeys
są generowane automatycznie dla typów, które przyjmują protokół Codable
, ale tworząc własną implementację w powyższym przykładzie, pozwalamy naszemu dekoderowi dopasować lokalną publicationDate
skrzynki na wielbłądy z Codable
węża w przypadku publication_date
ponieważ jest ona dostarczana przez interfejs API.
SwiftyJSON
SwiftyJSON to platforma Swift zbudowana w celu wyeliminowania potrzeby opcjonalnego łączenia w normalną serializację JSON.
Możesz go pobrać tutaj: https://github.com/SwiftyJSON/SwiftyJSON
Bez SwiftyJSON Twój kod wyglądałby tak, aby znaleźć nazwę pierwszej książki w obiekcie 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
}
W SwiftyJSON jest to bardzo uproszczone:
let json = JSON(data: data)
if let bookName = json[0]["book"]["name"].string {
//We can now use the book name
}
Eliminuje to konieczność sprawdzania każdego pola, ponieważ zwróci zero, jeśli którekolwiek z nich jest nieprawidłowe.
Aby użyć SwiftyJSON, pobierz poprawną wersję z repozytorium Git - istnieje gałąź dla Swift 3. Po prostu przeciągnij „SwiftyJSON.swift” do swojego projektu i zaimportuj do swojej klasy:
import SwiftyJSON
Możesz utworzyć obiekt JSON przy użyciu następujących dwóch inicjatorów:
let jsonObject = JSON(data: dataObject)
lub
let jsonObject = JSON(jsonObject) //This could be a string in a JSON format for example
Aby uzyskać dostęp do swoich danych, użyj indeksów dolnych:
let firstObjectInAnArray = jsonObject[0]
let nameOfFirstObject = jsonObject[0]["name"]
Następnie możesz przeanalizować swoją wartość do określonego typu danych, który zwróci wartość opcjonalną:
let nameOfFirstObject = jsonObject[0]["name"].string //This will return the name as a string
let nameOfFirstObject = jsonObject[0]["name"].double //This will return null
Możesz także skompilować swoje ścieżki w szybką tablicę:
let convolutedPath = jsonObject[0]["name"][2]["lastName"]["firstLetter"].string
Jest taki sam jak:
let convolutedPath = jsonObject[0, "name", 2, "lastName", "firstLetter"].string
SwiftyJSON ma również funkcję drukowania własnych błędów:
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
}
Jeśli chcesz napisać do obiektu JSON, możesz ponownie użyć indeksów dolnych:
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
Jeśli potrzebujesz oryginalnego ciągu dla JSON, na przykład jeśli chcesz go zapisać do pliku, możesz uzyskać surową wartość:
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
Freddy to biblioteka parsująca JSON prowadzona przez Big Nerd Ranch . Ma trzy główne zalety:
Bezpieczeństwo typu: pomaga w pracy z wysyłaniem i odbieraniem JSON w sposób zapobiegający awariom w czasie wykonywania.
Idiomatic: Wykorzystuje cechy ogólne, wyliczenia i funkcje funkcjonalne Swift, bez skomplikowanej dokumentacji ani magicznych niestandardowych operatorów.
Obsługa błędów: Zawiera informacje o błędach typowych błędów JSON.
Przykładowe dane JSON
Zdefiniujmy przykładowe dane JSON do użycia z tymi przykładami.
{ "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)!
Deserializowanie surowych danych
Aby dokonać deserializacji danych, inicjujemy obiekt JSON
a następnie uzyskujemy dostęp do określonego klucza.
do { let json = try JSON(data: jsonData) let success = try json.bool("success") } catch { // do something with the error }
try
tutaj, ponieważ dostęp do json
dla klucza "success"
może się nie powieść - może nie istnieć lub wartość może nie być wartością logiczną.
Możemy również określić ścieżkę dostępu do elementów zagnieżdżonych w strukturze JSON. Ścieżka to rozdzielona przecinkami lista kluczy i indeksów opisujących ścieżkę do interesującej wartości.
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 }
Bezpośrednia deserializacja modeli
JSON można bezpośrednio parsować do klasy modelu, która implementuje protokół 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 }
Serializacja surowych danych
Każda wartość JSON
może być serializowana bezpośrednio do NSData
.
let success = JSON.Bool(false) let data: NSData = try success.serialize()
Bezpośrednie serializowanie modeli
Każda klasa modelu, która implementuje protokół JSONEncodable
, może być serializowana bezpośrednio do 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()
Strzałka
Arrow to elegancka biblioteka parsująca JSON w Swift.
Pozwala na parsowanie JSON i mapowanie go do niestandardowych klas modeli za pomocą operatora <--
:
identifier <-- json["id"]
name <-- json["name"]
stats <-- json["stats"]
Przykład:
Szybki model
struct Profile {
var identifier = 0
var name = ""
var link: NSURL?
var weekday: WeekDay = .Monday
var stats = Stats()
var phoneNumbers = [PhoneNumber]()
}
Plik 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"
}]
}
Mapowanie
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"]
}
}
Stosowanie
let profile = Profile()
profile.deserialize(json)
Instalacja:
Kartagina
github "s4cha/Arrow"
CocoaPods
pod 'Arrow'
use_frameworks!
Ręcznie
Po prostu skopiuj i wklej Arrow.swift w swoim projekcie Xcode
https://github.com/s4cha/Arrow
Jako ramy
Pobierz Arrow z repozytorium GitHub i zbuduj cel Framework na przykładowym projekcie. Następnie połącz z tymi ramami.
Proste parsowanie JSON do niestandardowych obiektów
Nawet jeśli biblioteki innych firm są dobre, protokoły zapewniają prosty sposób na parsowanie JSON. Możesz sobie wyobrazić, że masz obiekt Todo
jako
struct Todo {
let comment: String
}
Ilekroć otrzymasz JSON, możesz obsłużyć zwykły NSData
jak pokazano w innym przykładzie, używając obiektu NSJSONSerialization
.
Następnie za pomocą prostego protokołu JSONDecodable
typealias JSONDictionary = [String:AnyObject]
protocol JSONDecodable {
associatedtype Element
static func from(json json: JSONDictionary) -> Element?
}
I sprawienie, by struktura Todo
zgodna z 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)
}
}
Możesz spróbować z tym kodem json:
{
"todos": [
{
"comment" : "The todo comment"
}
]
}
Po otrzymaniu go z interfejsu API można go serializować, tak jak w poprzednich przykładach pokazanych w instancji AnyObject
. Następnie możesz sprawdzić, czy instancja jest instancją JSONDictionary
guard let jsonDictionary = dictionary as? JSONDictionary else { return }
Inną rzeczą do sprawdzenia, specyficzną dla tego przypadku, ponieważ masz tablicę Todo
w JSON, jest słownik todos
guard let todosDictionary = jsonDictionary["todos"] as? [JSONDictionary] else { return }
Teraz, gdy masz tablicę słowników, możesz przekonwertować każdy z nich w obiekt Todo
za pomocą flatMap
(automatycznie usunie wartości nil
z tablicy)
let todos: [Todo] = todosDictionary.flatMap { Todo.from(json: $0) }
JSON Parsing Swift 3
Oto plik JSON, którego będziemy używać, o nazwie 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?"
} ] }
Zaimportuj plik JSON do swojego projektu
Możesz wykonać tę prostą funkcję, aby wydrukować plik 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)
}
}
}
}
Jeśli chcesz umieścić go w widoku tabeli, najpierw stworzę słownik z obiektem NSObject.
Utwórz nowy szybki plik o nazwie ParsingObject
i utwórz zmienne łańcuchowe.
Upewnij się, że nazwa zmiennej jest taka sama jak plik JSON
. Na przykład w naszym projekcie mamy name
i question
więc użyjemy go w naszym nowym pliku szybkim
var name: String?
var question: String?
Zainicjuj obiekt NSObject, który utworzyliśmy z powrotem w naszym var var ViewController.swift = ParsingObject Następnie wykonamy tę samą metodę, którą mieliśmy wcześniej, z niewielką modyfikacją.
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)
}
}
Następnie pokazujemy to w naszym widoku tabeli, robiąc to,
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
}