Einfache und saubere Methode zum Konvertieren von JSON-Zeichenfolgen in Object in Swift

82

Ich habe seit Tagen gesucht, um eine ziemlich einfache JSON-Zeichenfolge in einen Objekttyp in Swift zu konvertieren, aber ohne Erfolg.

Hier ist der Code für den Webdienstaufruf:

func GetAllBusiness() {

        Alamofire.request(.GET, "http://MyWebService/").responseString { (request, response, string, error) in

                println(string)

        }
}

Ich habe eine schnelle Struktur Business.swift:

struct Business {
    var Id : Int = 0
    var Name = ""
    var Latitude = ""
    var Longitude = ""
    var Address = ""
}

Hier ist mein Testdienst bereitgestellt:

[
  {
    "Id": 1,
    "Name": "A",
    "Latitude": "-35.243256",
    "Longitude": "149.110701",
    "Address": null
  },
  {
    "Id": 2,
    "Name": "B",
    "Latitude": "-35.240592",
    "Longitude": "149.104843",
    "Address": null
  }
  ...
]

Es wäre eine Freude, wenn mich jemand durch diese Sache führen würde.

Vielen Dank.

Hasan Nizamani
quelle

Antworten:

55

Hier sind einige Tipps, wie Sie mit einem einfachen Beispiel beginnen können.

Angenommen, Sie haben folgende JSON-Array-Zeichenfolge (ähnlich Ihrer) wie:

 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    

[BEARBEITEN]

Um null loszuwerden, wurde geändert in:

b.name = (json["name"] as AnyObject? as? String) ?? ""
b.id  =  (json["id"]  as AnyObject? as? Int) ?? 0 

Siehe auch Referenz von Coalescing Operator(aka ??)

Hoffe, es wird Ihnen helfen, die Dinge zu klären,

Maxim Shoustin
quelle
Genial! Lief wie am Schnürchen. Vielen Dank! Nur eine winzige Sache, es gibt einen Fehler, wenn das Element in JSON null ist. Wie in: b.name = json ["name"] als AnyObject! as String Wenn name null ist, wie kann ich eine Bedingung hinzufügen, um sie nullbar zu machen?
Hasan Nizamani
Was muss in AnyObject umgewandelt werden, bevor in String umgewandelt wird?
Bateramos
@Bateramos nichts. Sie erhalten optional AnyObjectper Schlüssel, stellen Sie jedoch sicher, dass dies nicht der Fall ist, bevor Sie auf String übertragen nil. Aus diesem Grund kann ich Sie mit !oder in meinem Fall ?mit Holdplace??
Maxim Shoustin
Ich denke, wenn Sie ein Objekt var b:Business = Business()außerhalb der Seitenschleife erstellen , werden möglicherweise in jedem Listenelement dieselben Daten angezeigt.
Patriks
49

für schnelle 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)
    }
}

Anwendungsbeispiel:

 let dict = myString.toJSON() as? [String:AnyObject] // can be any type here
FlowUI. SimpleUITesting.com
quelle
2
Zukunft Fußnote: statt do-catch, try?kann hier verwendet werden, das das gleiche Ergebnis wie die Rückkehr null in geben catch.
Okhan Okbay
... und wie genau greift man nach dieser Konvertierung auf einen Parameter zu?
Starwave
Für alle anderen Interessierten: Lassen Sie jsonObjectAsNSDictionary = responseString? .ToJSON () as! [String: AnyObject] print (jsonObjectAsNSDictionary ["Berechtigungen"]! ["Canaddeditowncomment"])
Starwave
1
Bitte korrigieren Sie die schnelle Syntax ... 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) } }
Yasir Ali
27

Als einfache String-Erweiterung sollte genügen:

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
        }
    }
}

Dann:

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";
}
*/
PassKit
quelle
15

Swift 4 analysiert JSON viel eleganter. Nehmen Sie einfach das codierbare Protokoll für Ihre Struktur gemäß diesem vereinfachten Beispiel an:

struct Business: Codable {
    let id: Int
    let name: String
}

Um das JSON-Array zu analysieren, teilen Sie dem Decoder mit, welche Objekte das Datenarray enthält

let parsedData = decoder.decode([Business].self, from: data)

Hier ist ein voll funktionsfähiges Beispiel:

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)

Weitere Hintergrundinformationen finden Sie in dieser hervorragenden Anleitung .

Noyer282
quelle
Der Nachteil dabei ist, dass man die Struktur im Auge behalten muss und wenn man ~ 30 oder mehr Parameter hat, wird das Verwalten zu einem großen Aufwand.
Starwave
@Starwave: Sie sind sich nicht sicher, ob dies Ihr Problem angeht. Beachten Sie jedoch, dass Sie nur die Felder, die Sie interessieren, in die Struktur aufnehmen müssen, die Sie zum Dekodieren der Daten verwenden. Wenn der JSON beispielsweise die Form [{"id": 1, "name:" A "," location ":" Warsaw "}] hätte, könnten Sie ihn dennoch mit derselben Geschäftsstruktur dekodieren. Das Standortfeld wäre einfach ignoriert.
Noyer282
Verdammt, daran habe ich nicht gedacht ... Aber was sind dann die analysierten Daten hier? NSDictionary?
Starwave
@ Starwave: Es ist ein Array von Strukturen, genau wie in der ursprünglichen Frage.
Noyer282
9

Für Swift 4

Ich habe die Logik von @ Passkit verwendet , musste aber gemäß Swift 4 aktualisieren


Schritt 1: Erweiterung für String-Klasse erstellt

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
            }
        }
    }

Schritt 2 So habe ich es in meinem View Controller verwendet

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])")

Das gesamte Guthaben geht an den ursprünglichen Benutzer. Ich habe gerade die neueste schnelle Version aktualisiert

swiftBoy
quelle
7

Ich habe eine Bibliothek geschrieben, die das Arbeiten mit JSON-Daten und die Deserialisierung in Swift zum Kinderspiel macht. Sie können es hier herunterladen : https://github.com/isair/JSONHelper

Bearbeiten: Ich habe meine Bibliothek aktualisiert. Jetzt können Sie Folgendes tun:

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
}

Alte Antwort:

Verwenden Sie zunächst anstelle von .responseString .response, um ein Antwortobjekt abzurufen. Ändern Sie dann Ihren Code in:

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. 
        }
    }
}

Und Sie müssen eine Business-Klasse wie diese erstellen:

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"]
    }
}

Die vollständige Dokumentation finden Sie auf meinem GitHub-Repo. Habe Spaß!

isair
quelle
Vielen Dank! Ich suchte jedoch eher nach einer benutzerdefinierten Methode, da es sich um ein Universitätsprojekt handelt, an dem ich arbeite, und mein Vorgesetzter wird nicht sehr glücklich sein, wenn er sieht, dass ich eine API verwende.
Hasan Nizamani
Wird Ihre Bibliothek für meine privaten Projekte verwenden. Vielen Dank!!
Hasan Nizamani
Ich bin froh, dass ich auf die eine oder andere Weise helfen konnte. Viel Glück bei Ihren Projekten! ^^
Isair
5

Für Swift 4 habe ich diese Erweiterung mit dem Codable- Protokoll geschrieben:

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)
Danny Narváez
quelle
4

Für iOS 10& Swift 3mit 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")
  }
}

und hier ist die Kategorieklasse

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
    }
}

IMO, dies ist bei weitem die eleganteste Lösung.

Zeitlos
quelle
2
let jsonString = "{\"id\":123,\"Name\":\"Munish\"}"

String in NSData konvertieren

 var data: NSData =jsonString.dataUsingEncoding(NSUTF8StringEncoding)!

 var error: NSError?

Konvertieren Sie NSData in 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)")
Munish Kapoor
quelle
2

Ich mag die Antwort von RDC, aber warum sollte der zurückgegebene JSON nur Arrays auf der obersten Ebene enthalten? Ich musste ein Wörterbuch auf der obersten Ebene zulassen, also habe ich es folgendermaßen geändert:

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
        }
    }
MR_22
quelle
2

SWIFT4 - Einfache und elegante Methode zum Dekodieren von JSON-Zeichenfolgen in Struct.

Erster Schritt - Codieren Sie den String in Daten mit der .utf8-Codierung.

Als dekodieren Ihre Daten zu 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 {}
Vitya Shurapov
quelle
1

Sie können swift.quicktype.io zum Konvertieren JSONin entweder structoder verwenden class. Sogar Sie können die Version von Swift to Genrate Code erwähnen.

Beispiel JSON:

{
  "message": "Hello, World!"
}

Generierter Code:

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
    }
}
Rugmangathan
quelle
1

Mit der SwiftyJSON- Bibliothek könnten Sie es so machen

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)
        }
    }
} 
Mohamed Saleh
quelle
0

Hier ist ein Beispiel, mit dem Sie die Dinge einfacher und einfacher machen können. Meine String-Daten in meiner Datenbank sind eine JSON-Datei, die folgendermaßen aussieht:

[{"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"}]

Befolgen Sie diese einfachen Schritte, um diese JSON-Zeichenfolgendaten zu laden. Erstellen Sie zunächst eine Klasse für mein MoreData-Objekt wie folgt:

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
}}

Zweitens erstellen Sie meine String-Erweiterung für meinen JSON-String wie folgt:

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)
}}

Drittens erstellen Sie die My Srevices-Klasse, um meine String-Daten wie folgt zu verarbeiten:

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
}}

Rufen Sie abschließend diese Funktion vom View Controller aus auf, um Daten wie folgt in die Tabellenansicht zu laden:

    func handlingJsonStringData(){
    moreData.removeAll(keepingCapacity: false)
    moreData =  Services.instance.loadMoreDataByString(byString: jsonString)
    print(self.moreData.count)
    tableView.reloadData()
}
hardiBSalih
quelle
0

Es könnte jemandem helfen. Ähnliches Beispiel.

Dies ist unsere CodableKlasse zum Binden von Daten. Sie können diese Klasse einfach mit SwiftyJsonAccelerator erstellen

 class ModelPushNotificationFilesFile: Codable {

  enum CodingKeys: String, CodingKey {
    case url
    case id
    case fileExtension = "file_extension"
    case name
  }

  var url: String?
  var id: Int?
  var fileExtension: String?
  var name: String?

  init (url: String?, id: Int?, fileExtension: String?, name: String?) {
    self.url = url
    self.id = id
    self.fileExtension = fileExtension
    self.name = name
  }

  required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    url = try container.decodeIfPresent(String.self, forKey: .url)
    id = try container.decodeIfPresent(Int.self, forKey: .id)
    fileExtension = try container.decodeIfPresent(String.self, forKey: .fileExtension)
    name = try container.decodeIfPresent(String.self, forKey: .name)
  }

}

Dies ist Json String

    let jsonString = "[{\"name\":\"\",\"file_extension\":\"\",\"id\":10684,\"url\":\"https:\\/\\/homepages.cae.wisc.edu\\/~ece533\\/images\\/tulips.png\"},

{\"name\":\"\",\"file_extension\":\"\",\"id\":10684,\"url\":\"https:\\/\\/homepages.cae.wisc.edu\\/~ece533\\/images\\/arctichare.png\"},

{\"name\":\"\",\"file_extension\":\"\",\"id\":10684,\"url\":\"https:\\/\\/homepages.cae.wisc.edu\\/~ece533\\/images\\/serrano.png\"},

{\"name\":\"\",\"file_extension\":\"\",\"id\":10684,\"url\":\"https:\\/\\/homepages.cae.wisc.edu\\/~ece533\\/images\\/peppers.png\"},

{\"name\":\"\",\"file_extension\":\"\",\"id\":10684,\"url\":\"https:\\/\\/homepages.cae.wisc.edu\\/~ece533\\/images\\/pool.png\"}]"

Hier konvertieren wir zu einem schnellen Objekt.

   let jsonData = Data(jsonString.utf8)

        let decoder = JSONDecoder()

        do {
            let fileArray = try decoder.decode([ModelPushNotificationFilesFile].self, from: jsonData)
            print(fileArray)
            print(fileArray[0].url)
        } catch {
            print(error.localizedDescription)
        }
Shourob Datta
quelle