Простой и понятный способ преобразовать строку JSON в объект в Swift

85

Я искал несколько дней, чтобы преобразовать довольно простую строку 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(aka ??)

Надеюсь, это поможет вам разобраться,

Максим Шустин
источник
Потрясающе! Работал как шарм. Благодаря! Всего лишь одна крошечная вещь, выдает ошибку, если элемент в JSON имеет значение NULL. Как в: b.name = json ["name"] как AnyObject! as String Если name имеет значение null, как я могу добавить условие, чтобы сделать его допускающим значение null?
Хасан Низамани 02
что нужно преобразовать в AnyObject перед преобразованием в String?
Bateramos, 04
@Bateramos ничего. Вы получаете необязательный AnyObjectключ, просто убедитесь, прежде чем понижать до String, что это не так nil. По этой причине я могу инкапсулировать, используя !или, в моем случае, ?с удерживающим устройством??
Максим Шустин
Я думаю, если вы создадите объект var b:Business = Business()вне бокового цикла, он может отображать одни и те же данные в каждом элементе списка.
Patriks
51

для 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)
    }
}

Пример использования:

 let dict = myString.toJSON() as? [String:AnyObject] // can be any type here
Просто кодер
источник
2
Сноска на будущее: вместо do-catch, здесь try?можно использовать, что даст тот же результат, что и возврат nil в catch.
Охан Окбай
... и как именно получить доступ к параметру после этого преобразования?
Starwave
Для всех, кто заинтересован: пусть jsonObjectAsNSDictionary = responseString? .ToJSON () as! [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

Простого расширения String должно хватить:

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";
}
*/
PassKit
источник
15

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

Для получения дополнительной информации ознакомьтесь с этим отличным руководством .

Нойер282
источник
Обратной стороной этого является то, что нужно отслеживать структуру, и если у вас есть ~ 30 или более параметров, управление станет большой проблемой.
Starwave
@Starwave: Не уверен, что это решает вашу проблему, но обратите внимание, что вам нужно только включить поля, которые вам нужны, в структуру, которую вы используете для декодирования данных. Например, если бы JSON имел форму [{"id": 1, "name:" A "," location ":" Warsaw "},], вы все равно могли бы декодировать его, используя ту же бизнес-структуру. Поле location будет просто игнорируется
Noyer282 08
Черт побери, я не подумал об этом ... Но тогда что это за parsedData? NSDictionary?
Starwave
@Starwave: это массив структур, как и в исходном вопросе.
Noyer282,
9

Для Swift 4

Я использовал логику @ Passkit , но мне пришлось обновить в соответствии с Swift 4


Шаг.1 Созданное расширение для класса String

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

Вся заслуга принадлежит первоначальному пользователю, я только что обновился до последней быстрой версии

SwiftBoy
источник
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. Радоваться, веселиться!

Isair
источник
Благодаря! Но я больше искал индивидуальный способ сделать это, так как это университетский проект, над которым я работаю, и мой руководитель не очень обрадуется, если увидит, что я использую 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
    }
}

ИМО, это, безусловно, самое элегантное решение.

Вне времени
источник
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
        }
    }
MR_22
источник
2

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

Первый шаг - кодируйте String в Data с кодировкой .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, выполните следующие простые шаги. Сначала создайте класс для моего объекта 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 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()
}
HardiBSalih
источник
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

    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)
        }
Шуроб Датта
источник
0

Используйте swiftyJson swiftyJson

platform :ios, '8.0'
use_frameworks!

target 'MyApp' do
pod 'SwiftyJSON', '~> 4.0'
end

Применение

import SwiftyJSON

let json = JSON(jsonObject)

let id = json["Id"].intValue
let name = json["Name"].stringValue
let lat = json["Latitude"].stringValue
let long = json["Longitude"].stringValue
let address = json["Address"].stringValue
            
print(id)
print(name)
print(lat)
print(long)
print(address)
Команда чанг
источник