如何在Swift 5中使用Alamofire解析JSON

3

我是iOS编程语言的新手,我正在尝试将WordPress JSON中的数据提取到表格视图中。 我遇到了错误:

类型'Any'的值没有下标

当我尝试实例化数组对象时会出现此错误。

这是JSON:

[
 {
    "id": 1352,
    "date": "2019-10-16T09:30:39",
    "date_gmt": "2019-10-16T09:30:39",
    "guid": {
        "rendered": "https://wepress.comm-it.it/ddjhgtr/"
    },
    "modified": "2019-10-16T13:23:41",
    "modified_gmt": "2019-10-16T13:23:41",
    "slug": "ddjhgtr",
    "status": "publish",
    "type": "post",
    "link": "https://wepress.comm-it.it/ddjhgtr/",
    "title": "ddjhgtr",
    "content": "eryyreytyvggjggvhghhh",
    "excerpt": "eryyreyty",
    "author": 2,
    "featured_media": {
        "id": 1418,
        "url": "https://wepress.comm-it.it/wp-content/uploads/2019/10/10-62.jpeg"
    },
    "comment_status": "open",
    "ping_status": "open",
    "sticky": false,
    "template": "",
    "format": "standard",
    "meta": [],
    "categories": [
        {
            "id": 1,
            "name": "Uncategorized",
            "description": ""
        }
    ],
    "tags": [],
    "_links": {
        "self": [
            {
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/posts/1352"
            }
        ],
        "collection": [
            {
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/posts"
            }
        ],
        "about": [
            {
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/types/post"
            }
        ],
        "author": [
            {
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/users/2"
            }
        ],
        "replies": [
            {
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/comments?post=1352"
            }
        ],
        "version-history": [
            {
                "count": 3,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/posts/1352/revisions"
            }
        ],
        "predecessor-version": [
            {
                "id": 1419,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/posts/1352/revisions/1419"
            }
        ],
        "wp:featuredmedia": [
            {
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/media/1418"
            }
        ],
        "wp:attachment": [
            {
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/media?parent=1352"
            }
        ],
        "wp:term": [
            {
                "taxonomy": "category",
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/categories?post=1352"
            },
            {
                "taxonomy": "post_tag",
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/tags?post=1352"
            },
            {
                "taxonomy": "difficulty-level-course",
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/difficulty-level-course?post=1352"
            },
            {
                "taxonomy": "category-course",
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/category-course?post=1352"
            },
            {
                "taxonomy": "location-course",
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/location-course?post=1352"
            },
            {
                "taxonomy": "duration-course",
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/duration-course?post=1352"
            }
        ],
        "curies": [
            {
                "name": "wp",
                "href": "https://api.w.org/{rel}",
                "templated": true
            }
        ]
    }
 },

....(many other News objects)

{
    "id": 774,
    "date": "2019-10-07T07:30:51",
    "date_gmt": "2019-10-07T07:30:51",
    "guid": {
        "rendered": "https://wepress.comm-it.it/name-here/"
    },
    "modified": "2019-10-07T07:30:51",
    "modified_gmt": "2019-10-07T07:30:51",
    "slug": "name-here",
    "status": "publish",
    "type": "post",
    "link": "https://wepress.comm-it.it/name-here/",
    "title": "name here",
    "content": "desc here",
    "excerpt": "desc here",
    "author": 2,
    "featured_media": null,
    "comment_status": "open",
    "ping_status": "open",
    "sticky": false,
    "template": "",
    "format": "standard",
    "meta": [],
    "categories": [
        {
            "id": 1,
            "name": "Uncategorized",
            "description": ""
        }
    ],
    "tags": [],
    "_links": {
        "self": [
            {
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/posts/774"
            }
        ],
        "collection": [
            {
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/posts"
            }
        ],
        "about": [
            {
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/types/post"
            }
        ],
        "author": [
            {
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/users/2"
            }
        ],
        "replies": [
            {
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/comments?post=774"
            }
        ],
        "version-history": [
            {
                "count": 0,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/posts/774/revisions"
            }
        ],
        "wp:attachment": [
            {
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/media?parent=774"
            }
        ],
        "wp:term": [
            {
                "taxonomy": "category",
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/categories?post=774"
            },
            {
                "taxonomy": "post_tag",
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/tags?post=774"
            },
            {
                "taxonomy": "difficulty-level-course",
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/difficulty-level-course?post=774"
            },
            {
                "taxonomy": "category-course",
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/category-course?post=774"
            },
            {
                "taxonomy": "location-course",
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/location-course?post=774"
            },
            {
                "taxonomy": "duration-course",
                "embeddable": true,
                "href": "https://wepress.comm-it.it/wp-json/wp/v2/duration-course?post=774"
            }
        ],
        "curies": [
            {
                "name": "wp",
                "href": "https://api.w.org/{rel}",
                "templated": true
            }
        ]
      }
  }
]

目前我只关心获取“对象”的图像(featured_media)、标题和内容,并将它们放入tableView中。这里是我创建的结构体:

新闻代表了包含在JSON数组中的结构体。

struct News {
    public var id: Int
    public var title: String
    public var content: String
    public var image: FeaturedMedia
}

struct FeaturedMedia {
    public var id: Int
    public var url: String
}

这里是UITableViewCell类:

import UIKit

class NewsTableViewCell: UITableViewCell {
    @IBOutlet weak var newsImage: UIImageView!
    @IBOutlet weak var newsTitle: UILabel!
    @IBOutlet weak var newsContent: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
}

这里是 UITableViewController 类:

import UIKit
import Alamofire
import Alamofire_SwiftyJSON
import SwiftyJSON

class NewsTableViewController: UITableViewController {

    var newsList: [News] = [News]()

    func parseJsonNews() {
        DispatchQueue.main.async {
            Alamofire.request("link request", method: .get).responseJSON { (response) in
                switch response.result {
                case .success(let value):
                    let news = [value]
                    print(news) // here in console it prints correctly the json, starting with [<__NSArrayI 0x6000001a9e60 ....
                    for new in news {
                        let title = new["title"]
                        print(title)
                    }
                    print(newsss)

                    self.tableView.reloadData()
                case.failure(let error):
                    print(error.localizedDescription)
                }
            })
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        parseJsonNews()
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.newsList.count
    }


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "newsCell", for: indexPath) as? NewsTableViewCell


        // Configure the cell...
        let imageUrl = URL.init(string: newsList[indexPath.row].featuredMedia.url)
        cell?.newsTitle.text = self.newsList[indexPath.row].title
        cell?.newsContent.text = self.newsList[indexPath.row].content
        cell.newsImage.load(url: imageUrl!)
        return cell!
    }
}

extension UIImageView {
    func load(url: URL) {
        DispatchQueue.global().async { [weak self] in
            if let data = try? Data(contentsOf: url) {
                if let image = UIImage(data: data) {
                    DispatchQueue.main.async {
                        self?.image = image
                    }
                }
            }
        }
    }
}

我正在寻找一个解决方案,发现了许多解析字典JSON的方法,但在这种情况下,它是一个数组,所以我修改了代码,如你所读的parseJsonNews方法,但它没有正常工作。

非常感谢您的帮助。


也许这对你有用 -> https://medium.com/better-programming/json-parsing-in-swift-2498099b78f - emrcftci
你在哪一行出现了错误? - koen
你需要使用JsonDecoder来解析响应中的值。你所采用的结构体应该被制作成Codable,然后你可以使用JSONDecoder将它们解码到你的模型类中。如果你需要JSONDecoder的代码帮助,请告诉我。 - Swati
@EmreCiftci 谢谢,我会学习的! - R. Milos
@Swati,我从Alamofire改成了Codable的方式,看起来它能够工作!谢谢你。 - R. Milos
显示剩余2条评论
3个回答

3

您可以像这样使用Codable进行解码:

let data = try? JSONDecoder().decode([DummyData].self, from: jsonData)

但是首先,你的所有模型都必须符合Codable协议。例如:

struct DummyData: Codable {
    let id: Int
    let date, dateGmt: String
    let modified, modifiedGmt, slug, status: String
    let type: String
    let link: String
    let title, content, excerpt: String
    let author: Int
    let commentStatus, pingStatus: String
    let sticky: Bool
    let template, format: String

    enum CodingKeys: String, CodingKey {
        case id, date
        case dateGmt = "date_gmt"
        case modified
        case modifiedGmt = "modified_gmt"
        case slug, status, type, link, title, content, excerpt, author
        case commentStatus = "comment_status"
        case pingStatus = "ping_status"
        case sticky, template, format
    }
}

现在它可以工作了!但是当我启动模拟器时,它会正确设置表视图,但是当我滚动tableView时,行的高度会变得更大。 - R. Milos
听起来像是自动布局的问题。我无法提供帮助,因为我不知道具体细节,但是苹果公司有非常好的关于自动布局的文档。https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/index.html - Mahmut Acar
另外,您可以在UITableViewDelegateheightForRowAt函数中更改所有单元格的高度。https://developer.apple.com/documentation/uikit/uitableviewdelegate/1614998-tableview - Mahmut Acar

1
一些事情:

  1. The error value of type 'Any' has no subscripts, refers to your line let title = new["title"]. the response results from Alamofire in the .success enum returns a type of Any, this is true to the whole array of data that you fetched. The Any type in swift doesn't have any subscripts implementations (i.e: you cannot access vars inside it using the following syntax obj['MY_VAR_NAME'].

  2. In order to access the title from your news object like so let title = new["title"], you have to first cast the objects to a dictionary, this could be done like this:

            // ... 
            let news = [value]
            print(news)
            for new in news {
                if let obj = new as? [String: Any] {
                    let title = obj["title"]
                    print(title)
                }
            }
            // ...
    
  3. In order to parse/use your custom structs, they must first comply to swift's Codable protocol.

     /// Example show only News, but the same which be used for FeaturedMedia
      struct News {
        public var id: Int
        public var title: String
        public var content: String
        public var image: FeaturedMedia?
        init?(jsonString: String) {
           guard let data = jsonString.data(using: .utf8) else {
                 return nil
           }
           guard let object = News(data: data) else {
                    return nil
           }
           self = object
       }
    
       init?(data: Data) {
           guard let object = try? JSONDecoder().decode(News.self, from:  
            data) else {
                  return nil
           }
          self = object
      }
      // Optional, for custom key names (i.e: "image" instead of "featured_media"
      private enum CodingKeys: String, CodingKey {
             case id = "id"
             case image = "featured_media"
           // etc..
      }
    
    }
    
完成第4步后,您可以像这样初始化您的对象:
            // ... 
            let news = [value]
            print(news)
            for new in news {
                if let obj = News(new) {
                  /// obj is now a News object
                    let title = obj.title
                    print(title)
                }
            }
            // ...

如果您正在追求更多信息(例如初始化整个数组),请查看this。希望我已经澄清了问题:)


-1
 func ComplainData() {
     let semaphore = DispatchSemaphore(value: 0)
     var request = URLRequest(url: URL(string: Constant.localBaseurl2 + "compID") !, timeoutInterval: Double.infinity)
     request.httpMethod = "GET"
     let task = URLSession.shared.dataTask(with: request) {
         data,
         response,
         error in
         if let response = response {
             let nsHTTPResponse = response as!HTTPURLResponse
             print(nsHTTPResponse)
         }

         if let error = error {
             print("\(error)")
             return
         }

         if let data = data {
             DispatchQueue.main.async {
                 let decoder = JSONDecoder()
                 decoder.keyDecodingStrategy = .convertFromSnakeCase //or any other Decoder\

                 do {
                     let jsonDecoder = JSONDecoder()
                     let memberRecord =
                         try jsonDecoder.decode(COMPLAINTSVC.GetComplaints.self, from: data)
                     print(memberRecord.message)

                     for detailData in memberRecord.message {
                         print(detailData)
                     }
                 } catch {
                     print(error.localizedDescription)
                 }
             }
         }
         semaphore.signal()
     }
     task.resume()
     semaphore.wait()
 }

1
你应该为答案添加一个解释。 - gurkan
如何使用Alamofire解析JSON。你的回答中没有使用Alamofire,这是离题了。 - Eric Aya

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