Je cherchais depuis des jours à convertir une chaîne JSON assez simple en un type d'objet dans Swift, mais sans résultat.
Voici le code pour l'appel du service Web:
func GetAllBusiness() {
Alamofire.request(.GET, "http://MyWebService/").responseString { (request, response, string, error) in
println(string)
}
}
J'ai une structure Swift Business.Swift:
struct Business {
var Id : Int = 0
var Name = ""
var Latitude = ""
var Longitude = ""
var Address = ""
}
Voici mon service de test déployé:
[
{
"Id": 1,
"Name": "A",
"Latitude": "-35.243256",
"Longitude": "149.110701",
"Address": null
},
{
"Id": 2,
"Name": "B",
"Latitude": "-35.240592",
"Longitude": "149.104843",
"Address": null
}
...
]
Ce serait un plaisir si quelqu'un me guide à travers cela.
Merci.
Voici quelques conseils pour commencer par un exemple simple.
Considérez que vous avez la chaîne de tableaux JSON (semblable à la vôtre) suivante:
var list:Array<Business> = []
// left only 2 fields for demo
struct Business {
var id : Int = 0
var name = ""
}
var jsonStringAsArray = "[\n" +
"{\n" +
"\"id\":72,\n" +
"\"name\":\"Batata Cremosa\",\n" +
"},\n" +
"{\n" +
"\"id\":183,\n" +
"\"name\":\"Caldeirada de Peixes\",\n" +
"},\n" +
"{\n" +
"\"id\":76,\n" +
"\"name\":\"Batata com Cebola e Ervas\",\n" +
"},\n" +
"{\n" +
"\"id\":56,\n" +
"\"name\":\"Arroz de forma\",\n" +
"}]"
// convert String to NSData
var data: NSData = jsonStringAsArray.dataUsingEncoding(NSUTF8StringEncoding)!
var error: NSError?
// convert NSData to 'AnyObject'
let anyObj: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0),
error: &error)
println("Error: \(error)")
// convert 'AnyObject' to Array<Business>
list = self.parseJson(anyObj!)
//===============
func parseJson(anyObj:AnyObject) -> Array<Business>{
var list:Array<Business> = []
if anyObj is Array<AnyObject> {
var b:Business = Business()
for json in anyObj as Array<AnyObject>{
b.name = (json["name"] as AnyObject? as? String) ?? "" // to get rid of null
b.id = (json["id"] as AnyObject? as? Int) ?? 0
list.append(b)
}// for
} // if
return list
}//func
[EDIT]
Pour se débarrasser de null changé en:
b.name = (json["name"] as AnyObject? as? String) ?? ""
b.id = (json["id"] as AnyObject? as? Int) ?? 0
Voir aussi Référence de Coalescing Operator (aka ??
)
J'espère que cela vous aidera à régler les problèmes,
pour Swift 3/4
extension String {
func toJSON() -> Any? {
guard let data = self.data(using: .utf8, allowLossyConversion: false) else { return nil }
return try? JSONSerialization.jsonObject(with: data, options: .mutableContainers)
}
}
Exemple d'utilisation:
let dict = myString.toJSON() as? [String:AnyObject] // can be any type here
Une simple extension de chaîne devrait suffire:
extension String {
var parseJSONString: AnyObject? {
let data = self.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
if let jsonData = data {
// Will return an object or nil if JSON decoding fails
return NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil)
} else {
// Lossless conversion of the string was not possible
return nil
}
}
}
Ensuite:
var jsonString = "[\n" +
"{\n" +
"\"id\":72,\n" +
"\"name\":\"Batata Cremosa\",\n" +
"},\n" +
"{\n" +
"\"id\":183,\n" +
"\"name\":\"Caldeirada de Peixes\",\n" +
"},\n" +
"{\n" +
"\"id\":76,\n" +
"\"name\":\"Batata com Cebola e Ervas\",\n" +
"},\n" +
"{\n" +
"\"id\":56,\n" +
"\"name\":\"Arroz de forma\",\n" +
"}]"
let json: AnyObject? = jsonString.parseJSONString
println("Parsed JSON: \(json!)")
println("json[3]: \(json![3])")
/* Output:
Parsed JSON: (
{
id = 72;
name = "Batata Cremosa";
},
{
id = 183;
name = "Caldeirada de Peixes";
},
{
id = 76;
name = "Batata com Cebola e Ervas";
},
{
id = 56;
name = "Arroz de forma";
}
)
json[3]: {
id = 56;
name = "Arroz de forma";
}
*/
J'ai écrit une bibliothèque qui facilite le travail avec les données json et la désérialisation dans Swift. Vous pouvez l'obtenir ici: https://github.com/isair/JSONHelper
Edit: J'ai mis à jour ma bibliothèque, vous pouvez maintenant le faire simplement avec ceci:
class Business: Deserializable {
var id: Int?
var name = "N/A" // This one has a default value.
required init(data: [String: AnyObject]) {
id <-- data["id"]
name <-- data["name"]
}
}
var businesses: [Business]()
Alamofire.request(.GET, "http://MyWebService/").responseString { (request, response, string, error) in
businesses <-- string
}
Ancienne réponse:
Tout d'abord, au lieu d'utiliser .responseString, utilisez .response pour obtenir un objet de réponse. Puis changez votre code pour:
func getAllBusinesses() {
Alamofire.request(.GET, "http://MyWebService/").response { (request, response, data, error) in
var businesses: [Business]?
businesses <-- data
if businesses == nil {
// Data was not structured as expected and deserialization failed, do something.
} else {
// Do something with your businesses array.
}
}
}
Et vous devez créer une classe Business comme ceci:
class Business: Deserializable {
var id: Int?
var name = "N/A" // This one has a default value.
required init(data: [String: AnyObject]) {
id <-- data["id"]
name <-- data["name"]
}
}
Vous pouvez trouver la documentation complète sur mon dépôt GitHub. S'amuser!
Pour Swift 4
J'ai utilisé la logique de @ Passkit mais j'ai dû mettre à jour conformément à Swift 4.
Étape 1 Extension créée pour la classe de chaîne
import UIKit
extension String
{
var parseJSONString: AnyObject?
{
let data = self.data(using: String.Encoding.utf8, allowLossyConversion: false)
if let jsonData = data
{
// Will return an object or nil if JSON decoding fails
do
{
let message = try JSONSerialization.jsonObject(with: jsonData, options:.mutableContainers)
if let jsonResult = message as? NSMutableArray
{
print(jsonResult)
return jsonResult //Will return the json array output
}
else
{
return nil
}
}
catch let error as NSError
{
print("An error occurred: \(error)")
return nil
}
}
else
{
// Lossless conversion of the string was not possible
return nil
}
}
}
Étape 2 Voici comment j'ai utilisé mon contrôleur de vue
var jsonString = "[\n" +
"{\n" +
"\"id\":72,\n" +
"\"name\":\"Batata Cremosa\",\n" +
"},\n" +
"{\n" +
"\"id\":183,\n" +
"\"name\":\"Caldeirada de Peixes\",\n" +
"},\n" +
"{\n" +
"\"id\":76,\n" +
"\"name\":\"Batata com Cebola e Ervas\",\n" +
"},\n" +
"{\n" +
"\"id\":56,\n" +
"\"name\":\"Arroz de forma\",\n" +
"}]"
//Convert jsonString to jsonArray
let json: AnyObject? = jsonString.parseJSONString
print("Parsed JSON: \(json!)")
print("json[2]: \(json![2])")
_ {Tout le crédit revient à l'utilisateur d'origine, je viens de mettre à jour la dernière version de Swift} _
Swift 4 analyse JSON de manière beaucoup plus élégante. Il suffit d’adopter le protocole codable pour votre structure selon cet exemple simplifié:
struct Business: Codable {
let id: Int
let name: String
}
Pour analyser le tableau JSON, vous indiquez au décodeur quels sont les objets du tableau de données.
let parsedData = decoder.decode([Business].self, from: data)
Voici un exemple de travail complet:
import Foundation
struct Business: Codable {
let id: Int
let name: String
}
// Generating the example JSON data:
let originalObjects = [Business(id: 0, name: "A"), Business(id: 1, name: "B")]
let encoder = JSONEncoder()
let data = try! encoder.encode(originalObjects)
// Parsing the data:
let decoder = JSONDecoder()
let parsedData = try! decoder.decode([Business].self, from: data)
Pour plus d'informations, consultez cet excellent guide .
Pour iOS 10
& Swift 3
, utilisez Alamofire & Gloss :
Alamofire.request("http://localhost:8080/category/en").responseJSON { response in
if let data = response.data {
if let categories = [Category].from(data: response.data) {
self.categories = categories
self.categoryCollectionView.reloadData()
} else {
print("Casting error")
}
} else {
print("Data is null")
}
}
et voici la classe Category
import Gloss
struct Category: Decodable {
let categoryId: Int?
let name: String?
let image: String?
init?(json: JSON) {
self.categoryId = "categoryId" <~~ json
self.name = "name" <~~ json
self.image = "image" <~~ json
}
}
OMI, c'est de loin la solution la plus élégante.
J'aime la réponse de RDC, mais pourquoi limiter le retour JSON pour n'avoir que des tableaux au niveau supérieur? J'avais besoin d'autoriser un dictionnaire au plus haut niveau, donc je l'ai modifié ainsi:
extension String
{
var parseJSONString: AnyObject?
{
let data = self.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
if let jsonData = data
{
// Will return an object or nil if JSON decoding fails
do
{
let message = try NSJSONSerialization.JSONObjectWithData(jsonData, options:.MutableContainers)
if let jsonResult = message as? NSMutableArray {
return jsonResult //Will return the json array output
} else if let jsonResult = message as? NSMutableDictionary {
return jsonResult //Will return the json dictionary output
} else {
return nil
}
}
catch let error as NSError
{
print("An error occurred: \(error)")
return nil
}
}
else
{
// Lossless conversion of the string was not possible
return nil
}
}
let jsonString = "{\"id\":123,\"Name\":\"Munish\"}"
Convertir une chaîne en NSData
var data: NSData =jsonString.dataUsingEncoding(NSUTF8StringEncoding)!
var error: NSError?
Convertir NSData en AnyObject
var jsonObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error)
println("Error: \\(error)")
let id = (jsonObject as! NSDictionary)["id"] as! Int
let name = (jsonObject as! NSDictionary)["name"] as! String
println("Id: \\(id)")
println("Name: \\(name)")
Pour Swift 4 , j’ai écrit cette extension en utilisant le protocole Codable :
struct Business: Codable {
var id: Int
var name: String
}
extension String {
func parse<D>(to type: D.Type) -> D? where D: Decodable {
let data: Data = self.data(using: .utf8)!
let decoder = JSONDecoder()
do {
let _object = try decoder.decode(type, from: data)
return _object
} catch {
return nil
}
}
}
var jsonString = "[\n" +
"{\n" +
"\"id\":72,\n" +
"\"name\":\"Batata Cremosa\",\n" +
"},\n" +
"{\n" +
"\"id\":183,\n" +
"\"name\":\"Caldeirada de Peixes\",\n" +
"},\n" +
"{\n" +
"\"id\":76,\n" +
"\"name\":\"Batata com Cebola e Ervas\",\n" +
"},\n" +
"{\n" +
"\"id\":56,\n" +
"\"name\":\"Arroz de forma\",\n" +
"}]"
let businesses = jsonString.parse(to: [Business].self)
Vous pouvez utiliser Swift.quicktype.io pour convertir JSON
en struct
ou class
. Même vous pouvez mentionner la version de Swift pour générer du code.
Exemple JSON:
{
"message": "Hello, World!"
}
Code généré:
import Foundation
typealias Sample = OtherSample
struct OtherSample: Codable {
let message: String
}
// Serialization extensions
extension OtherSample {
static func from(json: String, using encoding: String.Encoding = .utf8) -> OtherSample? {
guard let data = json.data(using: encoding) else { return nil }
return OtherSample.from(data: data)
}
static func from(data: Data) -> OtherSample? {
let decoder = JSONDecoder()
return try? decoder.decode(OtherSample.self, from: data)
}
var jsonData: Data? {
let encoder = JSONEncoder()
return try? encoder.encode(self)
}
var jsonString: String? {
guard let data = self.jsonData else { return nil }
return String(data: data, encoding: .utf8)
}
}
extension OtherSample {
enum CodingKeys: String, CodingKey {
case message
}
}
En utilisant SwiftyJSON library, vous pourriez le faire comme
if let path : String = Bundle.main.path(forResource: "tiles", ofType: "json") {
if let data = NSData(contentsOfFile: path) {
let optData = try? JSON(data: data as Data)
guard let json = optData else {
return
}
for (_, object) in json {
let name = object["name"].stringValue
print(name)
}
}
}
Voici un exemple pour vous rendre les choses plus simples et plus faciles. Mes données de chaîne dans ma base de données est un fichier JSON qui ressemble à ceci:
[{"stype":"noun","sdsc":"careless disregard for consequences","swds":"disregard, freedom, impulse, licentiousness, recklessness, spontaneity, thoughtlessness, uninhibitedness, unrestraint, wantonness, wildness","anwds":"restraint, self-restraint"},{"stype":"verb","sdsc":"leave behind, relinquish","swds":"abdicate, back out, bail out, bow out, chicken out, cop out, cut loose, desert, discard, discontinue, ditch, drop, drop out, duck, dump, dust, flake out, fly the coop, give up the ship, kiss goodbye, leave, leg it, let go, opt out, pull out, quit, run out on, screw, ship out, stop, storm out, surrender, take a powder, take a walk, throw over, vacate, walk out on, wash hands of, withdraw, yield","anwds":"adopt, advance, allow, assert, begin, cherish, come, continue, defend, favor, go, hold, keep, maintain, persevere, pursue, remain, retain, start, stay, support, uphold"},{"stype":"verb","sdsc":"leave in troubled state","swds":"back out, desert, disown, forsake, jilt, leave, leave behind, quit, reject, renounce, throw over, walk out on","anwds":"adopt, allow, approve, assert, cherish, come, continue, defend, favor, keep, pursue, retain, stay, support, uphold"}]
Pour charger ces données de chaîne JSON, suivez ces étapes simples. Tout d’abord, créez une classe pour mon objet MoreData comme ceci:
class MoreData {
public private(set) var stype : String
public private(set) var sdsc : String
public private(set) var swds : String
public private(set) var anwds : String
init( stype : String, sdsc : String, swds : String, anwds : String) {
self.stype = stype
self.sdsc = sdsc
self.swds = swds
self.anwds = anwds
}}
Deuxièmement, créez mon extension String pour mon JSON String comme ceci:
extension String {
func toJSON() -> Any? {
guard let data = self.data(using: .utf8, allowLossyConversion: false) else { return nil }
return try? JSONSerialization.jsonObject(with: data, options: .mutableContainers)
}}
Troisièmement, créez ma classe Srevices pour gérer mes données de chaîne comme ceci:
class Services {
static let instance: Services = Services()
func loadMoreDataByString(byString: String) -> [MoreData]{
var myVariable = [MoreData]()
guard let ListOf = byString.toJSON() as? [[String: AnyObject]] else { return [] }
for object in ListOf {
let stype = object["stype"] as? String ?? ""
let sdsc = object["sdsc"] as? String ?? ""
let swds = object["swds"] as? String ?? ""
let anwds = object["anwds"] as? String ?? ""
let myMoreData = MoreData(stype : stype, sdsc : sdsc, swds : swds, anwds : anwds)
myVariable.append(myMoreData)
}
return myVariable
}}
Enfin, appelez cette fonction à partir du contrôleur de vue pour charger les données dans la vue tableau de la manière suivante:
func handlingJsonStringData(){
moreData.removeAll(keepingCapacity: false)
moreData = Services.instance.loadMoreDataByString(byString: jsonString)
print(self.moreData.count)
tableView.reloadData()
}
Swift4 - Moyen simple et élégant de décoder des chaînes JSON vers Struct.
Première étape - encoder String to Data avec encodage .utf8.
Than décodez vos données dans YourDataStruct.
struct YourDataStruct: Codable {
let type, id: String
init(_ json: String, using encoding: String.Encoding = .utf8) throws {
guard let data = json.data(using: encoding) else {
throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
}
try self.init(data: data)
}
init(data: Data) throws {
self = try JSONDecoder().decode(YourDataStruct.self, from: data)
}
}
do { let successResponse = try WSDeleteDialogsResponse(response) }
} catch {}