将 JSON 解码为 Swift 模型而非根级别

Posted

技术标签:

【中文标题】将 JSON 解码为 Swift 模型而非根级别【英文标题】:Decoding JSON to Swift Model Not Root Level 【发布时间】:2019-01-01 01:32:27 【问题描述】:

我有以下型号:

struct Article: Decodable 

    let title: String
    let description: String
    let imageURL: String

    private enum CodingKeys: String, CodingKey 
        case title
        case description
        case imageURL = "urlToImage"
    


来自 URL 的 JSON 是这样的:


  status: "ok",
  totalResults: 70,
  articles: [
    
      source: 
        id: null,
        name: "Oilprice.com"
      ,
      author: "Tim Daiss",
      title: "$70 Oil Could Be Right Around The Corner | OilPrice.com - OilPrice.com",
      description: "A recent Bloomberg survey of oil analysts suggests that many believe oil could hit $70 per barrel in 2019, but are they just downplaying the bearish signals?",
      url: "https://oilprice.com/Energy/Crude-Oil/70-Oil-Could-Be-Right-Around-The-Corner.html",
      urlToImage: "https://d32r1sh890xpii.cloudfront.net/article/718x300/d7b8868e80d766d6a5d401219c65d6a0.jpg",
      publishedAt: "2019-01-01T00:00:08Z",
      content: "Oil markets have always been cyclical, and now even more so with advanced electronic trading, more speculation (which often results in wider oil price swings) and more producers, including the resurgence of U.S. oil production, now reaching over 11 million ba… [+4696 chars]"
    ,
    
      source: 
        id: "cnbc",
        name: "CNBC"
      ,
      author: "Jordan Novet",
      title: "Activision Blizzard plans to fire its CFO for an unspecified cause - CNBC",
      description: "Shares of gaming company Activision Blizzard moved lower Monday after it announced plans to let go of its chief financial officer.",
      url: "https://www.cnbc.com/2018/12/31/activision-blizzard-plans-to-fire-its-cfo-for-an-unspecified-cause.html",
      urlToImage: "https://fm.cnbc.com/applications/cnbc.com/resources/img/editorial/2012/08/02/48465125-activision-200.1910x1000.jpg",
      publishedAt: "2018-12-31T23:18:17Z",
      content: "Activision Blizzard shares moved down 1 percent in after-hours trading on Monday after the company said that it has informed its chief financial officer, Spencer Neumann, that it plans to let him go. For now he has been placed on a paid leave of absence. div … [+950 chars]"
    
  ]

我想要的只是 articles 键中的值。如何使用 Swift 4 JSONDecoder 获取它。

我知道如何通过创建父结构然后在父结构中创建“文章”属性来做到这一点。但是如果没有父结构,我怎么能做到这一点。

【问题讨论】:

***.com/questions/38864579/… @impression7vx 这是旧的做事方式。我正在寻找 JSONDecoder 方式。 我明白了。您正在尝试解析其中的一部分而不是整个内容。有趣的。你不想要一个父结构,这样你就可以使用totalResultsstatus 吗? "来自 URL 的 JSON 是这样的" 不,不是。您显示的不是有效的 JSON。 单独使用 JSONDecoder,如果没有某种外部结构,您将无法解码,因为您的结果将是一个 Article 数组,它是一个外部实体。因此,仅仅定义文章是不够的。 【参考方案1】:

单独使用 JSONDecoder,如果没有某种外部结构,您将无法解码,因为您的结果将是 Article 的 array,它是一个外部实体。因此,仅仅定义文章是不够的。

如果您不喜欢声明一个除了深入到 "articles" 键之外不需要任何其他目的的外部结构,这很容易通过在有限范围内临时声明它来解决向下钻取到 "articles" 键的范围。因此,您的程序的其余部分保留了 Article 结构,但外部结构不存在。

例如:

struct Article: Decodable 
    // ... your code here ...

func getArticles(_ d:Data) -> [Article] 
    struct Articles: Decodable  // this struct is temporary
        let articles:[Article]
    
    return try! JSONDecoder().decode(Articles.self, from: d).articles

其他代码现在可以看到 Article 结构并可以调用 getArticles 来解析 JSON 并接收 Article 数组,但其他代码永远不知道(也永远不会发现)存在额外的 Articles 结构;它仅作为一种局部存在于getArticles 函数中。它并不比在函数体内临时创建的任何其他局部变量更令人反感。

【讨论】:

【参考方案2】:

您可以尝试将JSONSerializationJSONDecoder 结合使用

do

    let tr = try JSONSerialization.jsonObject(with:data, options:[]) as! [String:Any] 
    guard let content =  tr["articles"] else  return 
    let articlesData = try JSONSerialization.data(withJSONObject:content, options: [])
    let res = try JSONDecoder().decode([Article].self, from: articlesData) 
    print(res) 

catch 

    print(error)

【讨论】:

【参考方案3】:

考虑重组您的数据。您需要构建数据模型以匹配 JSON 数据的数据结构。您可以只包含您想要的内容,但您必须包含您想要访问的属性的每个父级或级别。以 wikipedia API 中的以下示例为例。它打印出深入 JSON 数据结构的三个级别的 title 属性。从 JSON 示例代码中可以看出,它遗漏了几个属性,但它包括了我需要访问我想要的属性的每个父级。

import UIKit

struct Item: Decodable 
    var title: String


struct Search: Decodable 
    var search: [Item]


struct Result: Decodable 
    var query: Search


func getSearchResults()
    let url = URL(string: "https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=swift%204&utf8=&format=json")!

    URLSession.shared.dataTask(with: url)  (data, response, error) in
    if let urlResponse = response as? HTTPURLResponse, urlResponse.statusCode == 200 
        guard let data = data else  return 
        do 
            let results = try JSONDecoder().decode(Result.self, from: data)
            for item in results.query.search 
                print(item.title)
            
         catch let error as NSError 
            print("error: \(error)")
        
    
.resume()

getSearchResults()

JSON 示例:


"batchcomplete": "",
"continue": 
    "sroffset": 10,
    "continue": "-||"
,
"query": 
    "searchinfo": 
        "totalhits": 30349
    ,
    "search": [
        
            "ns": 0,
            "title": "Swift",
            "pageid": 219023,
            "size": 13896,
            "wordcount": 1496,
            "snippet": "The <span class=\"searchmatch\">swifts</span> are a family, Apodidae, of highly aerial birds. They are superficially similar to swallows, but are not closely related to any passerine species",
            "timestamp": "2018-12-28T21:29:44Z"
        ,
        
            "ns": 0,
            "title": "Swift (programming language)",
            "pageid": 42946389,
            "size": 49365,
            "wordcount": 5244,
            "snippet": "2015. <span class=\"searchmatch\">Swift</span> 3.0 was released on September 13, 2016. <span class=\"searchmatch\">Swift</span> <span class=\"searchmatch\">4</span>.0 was released on September 19, 2017. <span class=\"searchmatch\">Swift</span> <span class=\"searchmatch\">4</span>.1 was released on March 29, 2018. <span class=\"searchmatch\">Swift</span> won first",
            "timestamp": "2018-12-19T02:52:33Z"
        ,
        
            "ns": 0,
            "title": "Taylor Swift",
            "pageid": 5422144,
            "size": 237225,
            "wordcount": 18505,
            "snippet": "Taylor Alison <span class=\"searchmatch\">Swift</span> (born December 13, 1989) is an American singer-songwriter. One of the world's leading contemporary recording artists, she is known",
            "timestamp": "2018-12-26T21:55:51Z"
        ,

这是打印的输出:

//Swift
//Swift (programming language)
//Taylor Swift

【讨论】:

以上是关于将 JSON 解码为 Swift 模型而非根级别的主要内容,如果未能解决你的问题,请参考以下文章

Swift 4 - 解码期间字符串转换为大写

使用swift通过模型解析JSON到对象 - 解码int / string麻烦

Swift模型中数组和字典的JSON解码

Swift 4 Decodable - 将 JSON 对象解码为“数据”

解码 JSON (Swift) 时将单个字符串拆分为多个部分

使用 Alamofire 将 JSON 解码为对象