Простий і чистий спосіб перетворити рядок JSON на Object у Swift


82

Я кілька днів шукав, щоб перетворити досить простий рядок JSON на тип об’єкта в Swift, але безрезультатно.

Ось код для дзвінка через веб-службу:

func GetAllBusiness() {

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

                println(string)

        }
}

У мене є швидка структура Business.swift:

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

Ось розгорнута моя служба тестування:

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

Буде приємно, якщо хтось проведе мене через це.

Дякую.

Відповіді:


56

Ось кілька порад, як почати з простого прикладу.

Вважаємо, що у вас є такий рядок масиву JSON (схожий на ваш), як:

 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    

[РЕДАГУВАТИ]

Щоб позбутися від null змінено на:

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

Див. Також посилання на Coalescing Operator(також ??)

Сподіваюся, це допоможе вам розібратися,


Чудово! Працював як оберіг. Дякую! Лише одна крихітна річ - це помилка, якщо елемент у JSON є нульовим. Як у: b.name = json ["name"] як AnyObject! як рядок Якщо ім'я має значення null, як я можу додати умову, щоб зробити його анульованим?
Хасан Нізамані

яка необхідність для передачі в AnyObject перед тим, як передати у String?
Батерамос

@Bateramos нічого. Ви отримуєте необов’язковий AnyObjectключ, просто переконайтеся перед тим, як знизити String, що це ні nil. З цієї причини я можу інкапсулювати, використовуючи !або, у моєму випадку, ?місце??
Maxim Shoustin

Я думаю, якщо ви створите об'єкт var b:Business = Business()з бічного циклу, то він може відображати однакові дані в кожному елементі списку.
Патрікс

50

для стрімких 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)
    }
}

Приклад використання:

 let dict = myString.toJSON() as? [String:AnyObject] // can be any type here

2
Майбутня виноска: замість do-catch, try?тут можна використовувати, що дасть той самий результат, що і повернення nil catch.
Охан Окбай

... і як саме один отримує доступ до параметра після цього перетворення?
Starwave

Для всіх, кого цікавить: дозвольте jsonObjectAsNSDictionary = responseString? .ToJSON () як! [String: AnyObject] print (jsonObjectAsNSDictionary ["permissions"]! ["Canaddeditowncomment"])
Starwave

1
Будь ласка, виправте швидкий синтаксис ... 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) } }
Ясір Алі,

27

Оскільки простого розширення рядка має вистачити:

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

Тоді:

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

15

Стрімкий 4 аналізує JSON набагато елегантніше. Просто прийміть кодований протокол для вашої структури відповідно до цього спрощеного прикладу:

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

Щоб проаналізувати масив JSON, ви повідомляєте декодеру, що таке об’єкти масиву даних

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

Ось повний приклад роботи:

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)

Для отримання додаткової інформації перегляньте цей чудовий посібник .


Недоліком цього є те, що потрібно відстежувати структуру, і якщо у вас є ~ 30 або більше параметрів, управління стане великими клопотами.
Starwave

@Starwave: Не впевнений, що це стосується Вашого занепокоєння, але зауважте, що Вам потрібно лише включити поля, які Вас цікавлять, до структури, яку Ви використовуєте для декодування даних. Наприклад, якби JSON був у формі [{"id": 1, "name:" A "," location ":" Warsaw "},] ви все одно могли б його декодувати, використовуючи ту саму структуру бізнесу. Поле розташування просто було б ігнорується.
Noyer282,

Криваве пекло, я про це не думав ... Але тоді, що тут проаналізовані дані? NSD Dictionary?
Starwave

@Starwave: Це безліч структур, як і в оригінальному питанні.
Noyer282

9

Для Свіфта 4

Я використовував логіку @ Passkit , але мені довелося оновити відповідно до Swift 4


Крок.1 Створено розширення для String Class

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

Крок 2 Ось як я використовував у своєму контролері подання

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

Всі заслуги належать оригінальному користувачеві, я щойно оновився до останньої швидкої версії


7

Я написав бібліотеку, яка робить роботу з даними json та десеріалізацію легким у Swift. Ви можете отримати його тут: https://github.com/isair/JSONHelper

Редагувати: я оновив свою бібліотеку, тепер ви можете зробити це лише за допомогою цього:

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
}

Стара відповідь:

По-перше, замість .responseString, використовуйте .response, щоб отримати об'єкт відповіді. Потім змініть свій код на:

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

І вам потрібно скласти такий бізнес-клас:

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

Ви можете знайти повну документацію на моєму репозиторії GitHub. Веселіться!


Дякую! Але я більше шукав власний спосіб зробити це, оскільки це університетський проект, над яким я працюю, і мій керівник не буде особливо радий, якщо побачить, що я використовую API.
Хасан Нізамані

Буду використовувати вашу бібліотеку для моїх приватних проектів. Дякую!!
Хасан Нізамані

Радий, що зміг допомогти тим чи іншим способом. Успіхів у ваших проектах! ^^
isair

6

Для Swift 4 я написав це розширення, використовуючи протокол 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)

4

Для iOS 10& Swift 3, використовуючи 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")
  }
}

і ось клас 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
    }
}

IMO, це, безумовно, найелегантніше рішення.


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

Перетворити рядок у NSData

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

 var error: NSError?

Перетворити NSData на 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)")

2

Мені подобається відповідь RDC, але навіщо обмежувати повернення JSON лише масивами на верхньому рівні? Мені потрібно було дозволити словник на верхньому рівні, тому я змінив його таким чином:

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

2

SWIFT4 - простий та елегантний спосіб декодування рядків JSON до Struct.

Перший крок - кодування рядка до даних за допомогою кодування .utf8.

Потім розшифруйте ваші дані до 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 {}

1

Ви можете використовувати swift.quicktype.io для перетворення JSONв будь-який structабо class. Навіть ви можете згадати версію швидкого створення коду.

Приклад JSON:

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

Створений код:

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

1

Використовуючи бібліотеку SwiftyJSON , ви можете зробити це як

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

0

Ось зразок для вас, щоб зробити речі простішими та простішими. Дані мого рядка в моїй базі даних - це файл JSON, який виглядає так:

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

Щоб завантажити ці дані JSON String, дотримуйтесь цих простих кроків. Спочатку створіть клас для мого об'єкта MoreData таким чином:

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

По-друге, створіть моє розширення String для мого рядка JSON таким чином:

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

По-третє, створіть My Srevices Class для обробки моїх рядкових даних наступним чином:

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

Нарешті, викличте цю функцію з контролера перегляду, щоб завантажити дані у поданні таблиці таким чином:

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

0

Це може допомогти комусь. Подібний приклад.

Це наш Codableклас для прив’язки даних. Ви можете легко створити цей клас за допомогою SwiftyJsonAccelerator

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

}

Це 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\"}]"

Тут ми перетворюємо на швидкий об'єкт.

   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)
        }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.