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


Я искал в течение нескольких дней, чтобы преобразовать довольно простую строку JSON в тип объекта в Swift, но безрезультатно.

вот код для вызова веб-службы:

func GetAllBusiness() {

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

                println(string)

        }
}

у меня есть бизнес swift struct.Свифт:

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

было бы приятно, если кто-то наставит меня через это.

спасибо.

12 62

12 ответов:

вот несколько советов, как начать с простого примера.

считайте, что у вас есть следующая строка массива 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    

[EDIT]

чтобы избавиться от null изменен на:

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

см. также ссылки Коалесцирующий Оператор (он же ??)

надеюсь, что это поможет Вам разобраться,

как простое расширение строки должно быть достаточно:

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

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

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

Edit: я обновил свою библиотеку, теперь вы можете сделать это только с этим:

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, use .ответ, чтобы получить объект ответа. Затем измените свой код на:

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. Получайте удовольствие!

на Swift 2.2

я использовал @Passkitлогика, но я должен был обновить в соответствии с Swift 2


на iOS 10 & Swift 3, используя Alamofire & блеск:

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

а вот категория class

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

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

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)

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

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

преобразовать строку в NSData

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

 var error: NSError?

преобразовать формат NSData, чтобы какой-либо объект

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

Мне нравится ответ 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
        }
    }

вы можете использовать swift.quicktype.io для преобразования JSON или struct или class. Даже вы можете упомянуть версию swift для генерации кода.

пример 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
    }
}

С помощью 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)
        }
    }
} 

на Swift 4, Я написал это расширение с помощью возможностью протокол:

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)