Swift Language
Läsa och skriva JSON
Sök…
Syntax
- NSJSONSerialization.JSONObjectWithData (jsonData, alternativ: NSJSONReadingOptions) // Returnerar ett objekt från jsonData. Denna metod kastar på misslyckande.
- NSJSONSerialization.dataWithJSONObject (jsonObject, alternativ: NSJSONWritingOptions) // Returnerar NSData från ett JSON-objekt. Pass in NSJSONWritingOptions.PrettyPrinted i alternativ för en utgång som är mer läsbar.
JSON Serialisering, kodning och avkodning med Apple Foundation och Swift Standard Library
JSONSerialization- klassen är inbyggd i Apples Foundation-ramverk.
Läs JSON
Funktionen JSONObjectWithData
tar NSData
och returnerar AnyObject
. Du kan använda as?
för att konvertera resultatet till din förväntade 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)")
}
Du kan skicka options: .AllowFragments
istället för options: []
att tillåta läsning av JSON när toppnivån inte är en matris eller ordbok.
Skriv JSON
Ringa dataWithJSONObject
konverterar ett JSON-kompatibelt objekt (kapslade matriser eller ordböcker med strängar, siffror och NSNull
) till råa NSData
kodade som 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)")
}
Du kan skicka options: .PrettyPrinted
istället för options: []
för vackert utskrift.
Samma beteende i Swift 3 men med en annan syntax.
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)")
}
Obs: Följande är för närvarande endast tillgängligt i Swift 4.0 och senare.
Från och med Swift 4.0 inkluderar Swift-standardbiblioteket protokollen Encodable
och Decodable
att definiera en standardiserad strategi för datakodning och avkodning. Att anta dessa protokoll tillåter implementering av Encoder
och Decoder
tar dina data och kodar eller avkodar dem till och från en extern representation såsom JSON. Överensstämmelse med Codable
protokollet kombinerar både Encodable
och Decodable
protokoll. Detta är nu det rekommenderade sättet att hantera JSON i ditt program.
Koda och avkoda automatiskt
Det enklaste sättet att göra en typ kodbar är att förklara dess egenskaper som typer som redan är Codable
. Dessa typer inkluderar standardbibliotekstyper som String
, Int
och Double
; och Foundation-typer som Date
, Data
och URL
. Om egenskaperna för en typ är kodbara kommer själva typen att automatiskt överensstämma med Codable
genom att helt enkelt förklara överensstämmelsen.
Betrakta följande exempel, i vilka Book
struktur överensstämmer till Codable
.
struct Book: Codable {
let title: String
let authors: [String]
let publicationDate: Date
}
Observera att standardsamlingar som
Array
ochDictionary
överensstämmer medCodable
om de innehåller kodbara typer.
Genom att anta Codable
den Book
kan strukturen nu kodas till och avkodas från JSON med hjälp av Apple Foundation Classes JSONEncoder
och JSONDecoder
trots Book
i sig innehåller ingen kod för att specifikt hantera JSON. Anpassade kodare och avkodare kan också skrivas genom att Encoder
Decoder
respektive avkodarprotokollen.
Koda till JSON-data
// 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)
Ställ in
encoder.outputFormatting = .prettyPrinted
för enklare läsning. ## Avkodning från JSON-data
Avkoda från JSON-data
// 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)
I exemplet
Book.self
informerarBook.self
avkodaren om den typ som JSON ska avkodas till.
Kodning eller avkodning exklusivt
Ibland kanske du inte behöver data för att vara både kodbara och avkodningsbara, till exempel när du bara behöver läsa JSON-data från ett API, eller om ditt program bara skickar JSON-data till ett API.
Om du bara tänker skriva JSON-data ska du anpassa din typ till Encodable
.
struct Book: Encodable {
let title: String
let authors: [String]
let publicationDate: Date
}
Om du bara tänker läsa JSON-data ska du anpassa din typ till Decodable
.
struct Book: Decodable {
let title: String
let authors: [String]
let publicationDate: Date
}
Använda anpassade nyckelnamn
API: er använder ofta andra namnkonventioner än Swift-kamelväskan, till exempel ormfodral. Detta kan bli ett problem när det gäller att avkoda JSON, eftersom JSON-nycklarna som standard måste anpassa exakt till din typs egendomnamn. För att hantera dessa scenarier kan du skapa anpassade nycklar för din typ med CodingKey
protokollet.
struct Book: Codable {
// ...
enum CodingKeys: String, CodingKey {
case title
case authors
case publicationDate = "publication_date"
}
}
CodingKeys
genereras automatiskt för typer som antar Codable
protokollet, men genom att skapa vår egen implementering i exemplet ovan tillåter vi vår avkodare att matcha den lokala publicationDate
av kamelfallsdatum med publication_date
när den levereras av API.
SwiftyJSON
SwiftyJSON är ett Swift-ramverk som är byggt för att ta bort behovet av valfri kedja i normal JSON-seriellisering.
Du kan ladda ner det här: https://github.com/SwiftyJSON/SwiftyJSON
Utan SwiftyJSON skulle din kod se ut så här för att hitta namnet på den första boken i ett JSON-objekt:
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
}
I SwiftyJSON förenklas detta enormt:
let json = JSON(data: data)
if let bookName = json[0]["book"]["name"].string {
//We can now use the book name
}
Det tar bort behovet av att kontrollera alla fält, eftersom det kommer att returnera noll om någon av dem är ogiltiga.
För att använda SwiftyJSON, ladda ner rätt version från Git-förvaret - det finns en gren för Swift 3. Dra bara "SwiftyJSON.swift" till ditt projekt och importera till din klass:
import SwiftyJSON
Du kan skapa ditt JSON-objekt med följande två initialiseringar:
let jsonObject = JSON(data: dataObject)
eller
let jsonObject = JSON(jsonObject) //This could be a string in a JSON format for example
För att få åtkomst till dina data använder du abonnemang:
let firstObjectInAnArray = jsonObject[0]
let nameOfFirstObject = jsonObject[0]["name"]
Du kan sedan para ditt värde till en viss datatyp, vilket kommer att returnera ett valfritt värde:
let nameOfFirstObject = jsonObject[0]["name"].string //This will return the name as a string
let nameOfFirstObject = jsonObject[0]["name"].double //This will return null
Du kan också sammanställa dina vägar till en snabb Array:
let convolutedPath = jsonObject[0]["name"][2]["lastName"]["firstLetter"].string
Är det samma som:
let convolutedPath = jsonObject[0, "name", 2, "lastName", "firstLetter"].string
SwiftyJSON har också funktionalitet för att skriva ut sina egna fel:
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
}
Om du behöver skriva till ditt JSON-objekt kan du använda prenumerationer igen:
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
Om du behöver den ursprungliga strängen för JSON, till exempel om du behöver skriva den till en fil, kan du få ut råvärdet:
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 är ett JSON-analysbibliotek underhållet av Big Nerd Ranch . Det har tre huvudfördelar:
Typsäkerhet: Hjälper dig att arbeta med att skicka och ta emot JSON på ett sätt som förhindrar runtime-krasch.
Idiomatisk: Utnyttjar Swifts generika, uppräkningar och funktionella funktioner, utan komplicerad dokumentation eller magiska anpassade operatörer.
Felhantering: Tillhandahåller informativ felinformation för vanliga JSON-fel.
Exempel JSON-data
Låt oss definiera några exempel på JSON-data för användning med dessa exempel.
{ "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)!
Deserializing Raw Data
För att deserialisera data, initialiserar vi ett JSON
objekt och JSON
sedan en viss nyckel.
do { let json = try JSON(data: jsonData) let success = try json.bool("success") } catch { // do something with the error }
Vi try
här eftersom åtkomst till json
för nyckeln "success"
kan misslyckas - det kanske inte finns, eller värdet kanske inte är en booleska.
Vi kan också ange en sökväg för åtkomstelement som är kapslade i JSON-strukturen. Sökvägen är en kommaseparerad lista med nycklar och index som beskriver sökvägen till ett intressevärde.
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 }
Deserialisering av modeller direkt
JSON kan direkt analyseras till en JSONDecodable
som implementerar JSONDecodable
protokollet.
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 }
Serialisera rå data
Alla JSON
värden kan serialiseras direkt till NSData
.
let success = JSON.Bool(false) let data: NSData = try success.serialize()
Serialiserar modeller direkt
Alla JSONEncodable
som implementerar JSONEncodable
protokollet kan serialiseras direkt till 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()
Pil
Arrow är ett elegant JSON-parsing-bibliotek i Swift.
Det gör det möjligt att analysera JSON och kartlägga den till anpassade modellklasser med hjälp av en <--
operatör:
identifier <-- json["id"]
name <-- json["name"]
stats <-- json["stats"]
Exempel:
Snabb modell
struct Profile {
var identifier = 0
var name = ""
var link: NSURL?
var weekday: WeekDay = .Monday
var stats = Stats()
var phoneNumbers = [PhoneNumber]()
}
JSON-fil
{
"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"
}]
}
kartläggning
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"]
}
}
Användande
let profile = Profile()
profile.deserialize(json)
Installation:
Carthage
github "s4cha/Arrow"
CocoaPods
pod 'Arrow'
use_frameworks!
Manuellt
Kopiera och klistra in Arrow.swift i ditt Xcode-projekt
https://github.com/s4cha/Arrow
Som ett ramverk
Ladda ner Arrow från GitHub-lagret och bygg ramverkets mål på exempelprojektet. Länk sedan mot denna ram.
Enkel JSON-analysering i anpassade objekt
Även om tredjepartsbibliotek är bra, tillhandahålls ett enkelt sätt att analysera JSON genom protokoll. Du kan föreställa dig att du har ett objekt Todo
som
struct Todo {
let comment: String
}
När du får JSON kan du hantera vanliga NSData
som visas i det andra exemplet med NSJSONSerialization
objekt.
Efter det med ett enkelt protokoll JSONDecodable
typealias JSONDictionary = [String:AnyObject]
protocol JSONDecodable {
associatedtype Element
static func from(json json: JSONDictionary) -> Element?
}
Och att göra din Todo
struktur överensstämmer med JSONDecodable
gör tricket
extension Todo: JSONDecodable {
static func from(json json: JSONDictionary) -> Todo? {
guard let comment = json["comment"] as? String else { return nil }
return Todo(comment: comment)
}
}
Du kan prova det med den här jsonkoden:
{
"todos": [
{
"comment" : "The todo comment"
}
]
}
När du fick det från API: n kan du serialisera det som de tidigare exemplen som visas i en AnyObject
instans. Efter det kan du kontrollera om instansen är en JSONDictionary
instans
guard let jsonDictionary = dictionary as? JSONDictionary else { return }
Det andra att kontrollera, specifikt för detta fall eftersom du har en rad Todo
i JSON, är todos
ordboken
guard let todosDictionary = jsonDictionary["todos"] as? [JSONDictionary] else { return }
Nu när du har fått uppsättningen ordböcker kan du konvertera var och en av dem i ett Todo
objekt med hjälp av flatMap
(det raderar automatiskt nil
från arrayen)
let todos: [Todo] = todosDictionary.flatMap { Todo.from(json: $0) }
JSON Parsing Swift 3
Här är JSON-filen som vi kommer att använda som heter 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?"
} ] }
Importera din JSON-fil i ditt projekt
Du kan utföra den här enkla funktionen för att skriva ut din JSON-fil
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)
}
}
}
}
Om du vill lägga den i en tabellvy skulle jag skapa en ordbok först med ett NSObject.
Skapa en ny ParsingObject
heter ParsingObject
och skapa dina strängvariabler.
Se till att variabelns namn är samma som JSON-filen
. I vårt projekt har vi till exempel name
och question
så i vår nya snabbfil kommer vi att använda
var name: String?
var question: String?
Initialisera NSObject som vi gjorde tillbaka till vår ViewController.swift var array = ParsingObject Sedan skulle vi utföra samma metod som vi hade tidigare med en mindre modifiering.
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)
}
}
Sedan visar vi det i vår tabellvisning genom att göra detta,
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
}