在Swift中将JSON字符串转换为对象的简单而干净的方法

110

我已经搜索了几天,试图将一个相当简单的JSON字符串转换为Swift中的对象类型,但一直没有成功。

这是Web服务调用的代码:

func GetAllBusiness() {

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

                println(string)

        }
}

我有一个名为Business.swift的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
  }
  ...
]

如果有人能够指导我完成这个,那将是一件令人愉快的事情。

谢谢。

17个回答

72

适用于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
未来注释:在这里可以使用try?代替do-catch,它将产生与在catch中返回nil相同的结果。 - Okhan Okbay
1
在这种转换之后,如何精确地访问参数? - Starwave
1
对于其他有兴趣的人: 让 jsonObjectAsNSDictionary = responseString?.toJSON() as! [String:AnyObject] 打印(jsonObjectAsNSDictionary["permissions"]!["canaddeditowncomment"]) - Starwave
1
请纠正 Swift 语法...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

64

以下是一些如何从简单示例入手的提示。

假设你有如下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 

另请参阅合并运算符参考资料 (又称??)

希望这能帮助你梳理事情,


太棒了!像魔法一样顺利。谢谢!只有一个小问题,如果JSON中的元素为空,它会报错。比如:b.name = json["name"] as AnyObject! as String 如果name为空,我该如何添加条件使其可为空? - Hasan Nizamani
在进行字符串转换之前,为什么需要先将其强制转换为 AnyObject? - Bateramos
@Bateramos 什么也没有。你可以通过键获取可选 AnyObject,但在向下转换为字符串之前,请确保它不是 nil。出于这个原因,你可以使用占位符 ?? 来封装,使用 ! 或者在我的情况下使用 ? - Maxim Shoustin
我猜,如果你在循环外创建对象 var b:Business = Business(),那么每个列表元素中可能会显示相同的数据。 - Patriks
简单而清晰的方式 - Henadzi Rabkin

28

一个简单的字符串扩展应该就足够了:

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

20

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)

了解更多背景信息,请查看这篇优秀指南


1
这样做的缺点是,一个人必须跟踪该结构,如果您有30个或更多参数,则管理将变得非常麻烦。 - Starwave
@Starwave:不确定这是否解决了您的问题,但请注意,在用于解码数据的结构中,您只需要包含您关心的字段。例如,如果JSON的格式为[{"id": 1, "name: "A", "location": "Warsaw"},],您仍然可以使用相同的Business结构进行解码。位置字段将被忽略。 - Noyer282
我的天啊,我没有想到那个……但那么说,parsedData 是什么?NSDictionary 吗? - Starwave
@Starwave:这是一个结构体数组,就像原来的问题一样。 - Noyer282

11

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

第二步。 这是我在视图控制器中的使用方法

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

所有功劳归原作者所有,我只是为了最新版本的Swift做了更新。


9
对于 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)

7
我写了一个库,使在Swift中处理JSON数据和反序列化变得轻而易举。你可以在这里获取它: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. 
        }
    }
}

And you need to make a Business class like this:

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,他可能不会很高兴。 - Hasan Nizamani
我会在我的私人项目中使用你的库。谢谢! - Hasan Nizamani
很高兴我能在某种程度上帮到你。祝你的项目好运!^^ - isair

5

针对 iOS 10Swift 3,使用 AlamofireGloss

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

在我看来,这是迄今为止最优雅的解决方案。


1
使用Codable替换Gloss,因为Gloss已被归档(以支持Swift的Codable框架)。 - Mia

4

SWIFT4 - 将JSON字符串解码为结构体的简单而优雅的方法。

第一步 - 使用 .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 {}

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接