Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用codable的Swift JSON解码发现了一个字符串/数据_Swift_Codable - Fatal编程技术网

使用codable的Swift JSON解码发现了一个字符串/数据

使用codable的Swift JSON解码发现了一个字符串/数据,swift,codable,Swift,Codable,我正在玩《纽约时报》的API,我得到了消息 类型不匹配(Swift.Array,Swift.DecodingError.Context)(编码路径: [编码键(stringValue:“结果”,intValue:无), _JSONKey(stringValue:“索引3”,intValue:3),codingkey(stringValue:“多媒体”,intValue:nil)],debugDescription:“预期解码 数组,但找到了一个字符串/数据。“,underyingError:ni

我正在玩《纽约时报》的API,我得到了消息

类型不匹配(Swift.Array,Swift.DecodingError.Context)(编码路径: [编码键(stringValue:“结果”,intValue:无), _JSONKey(stringValue:“索引3”,intValue:3),codingkey(stringValue:“多媒体”,intValue:nil)],debugDescription:“预期解码 数组,但找到了一个字符串/数据。“,underyingError:nil))

当JSON的多媒体部分是字符串而不是数组时会发生这种情况:类似于这个问题:

所以我决定做一个最低限度的例子

附文章

public struct Article : Codable {
    var abstract: String?
    var thumbnail_standard: String?
    var multimedia: [Multimedia]?
    var title: String?
    var url: URL?

    private enum CodingKeys: String, CodingKey {
        case abstract = "abstract"
        case multimedia = "multimedia"
        case thumbnail_standard = "thumbnail_standard"
        case title = "title"
        case url = "url"
    }
}
多媒体

struct Multimedia: Codable {
    var url: String?

    private enum CodingKeys: String, CodingKey {
        case url = "url"
    }
}
我可以使用JSON字符串

        let jsonString = """
{
      "slug_name": "30dc-emoluments",
      "section": "U.S.",
      "subsection": "Politics",
      "title": "Congressional Democrats’ Lawsuit Examining Trump’s Private Business Can Proceed, Federal Judge Says",
      "abstract": "The decision is at least a temporary victory for the president’s critics who say he is willfully flaunting constitutional bans.",
      "url": "https://www.nytimes.com/2019/04/30/us/politics/trump-emoluments-clauses.html",
      "byline": "By SHARON LaFRANIERE",
      "thumbnail_standard": "https://static01.nyt.com/images/2019/04/19/world/30dc-emoluments/30dc-emoluments-thumbStandard.jpg",
      "item_type": "Article",
      "source": "The New York Times",
      "updated_date": "2019-04-30T22:09:45-04:00",
      "created_date": "2019-04-30T21:56:05-04:00",
      "published_date": "2019-04-29T20:00:00-04:00",
      "first_published_date": "2019-04-30T21:54:34-04:00",
      "material_type_facet": "News",
      "kicker": null,
      "subheadline": null,
      "des_facet": "",
      "org_facet": [
        "Democratic Party",
        "Constitution (US)",
        "Justice Department",
        "Trump International Hotel (Washington, DC)"
      ],
      "per_facet": [
        "Sullivan, Emmet G",
        "Trump, Donald J"
      ],
      "geo_facet": "",
      "related_urls": [
        {
          "suggested_link_text": "Appeals Court Judges Appear Skeptical of Emoluments Case Against Trump",
          "url": "https://www.nytimes.com/2019/03/19/us/politics/trump-emoluments-lawsuit.html"
        },
        {
          "suggested_link_text": "Democrats in Congress Sue Trump Over Foreign Business Dealings",
          "url": "https://www.nytimes.com/2017/06/14/us/politics/democrats-in-congress-to-sue-trump-over-foreign-business-dealings.html"
        }
      ],
      "multimedia": [
        {
          "url": "https://static01.nyt.com/images/2019/04/19/world/30dc-emoluments/30dc-emoluments-thumbStandard.jpg",
          "format": "Standard Thumbnail",
          "height": 75,
          "width": 75,
          "type": "image",
          "subtype": "photo",
          "caption": "The Trump International Hotel in Washington.",
          "copyright": "Gabriella Demczuk for The New York Times"
        },
        {
          "url": "https://static01.nyt.com/images/2019/04/19/world/30dc-emoluments/30dc-emoluments-articleInline.jpg",
          "format": "Normal",
          "height": 130,
          "width": 190,
          "type": "image",
          "subtype": "photo",
          "caption": "The Trump International Hotel in Washington.",
          "copyright": "Gabriella Demczuk for The New York Times"
        },
        {
          "url": "https://static01.nyt.com/images/2019/04/19/world/30dc-emoluments/30dc-emoluments-mediumThreeByTwo210.jpg",
          "format": "mediumThreeByTwo210",
          "height": 140,
          "width": 210,
          "type": "image",
          "subtype": "photo",
          "caption": "The Trump International Hotel in Washington.",
          "copyright": "Gabriella Demczuk for The New York Times"
        },
        {
          "url": "https://static01.nyt.com/images/2019/04/19/world/30dc-emoluments/30dc-emoluments-mediumThreeByTwo440.jpg",
          "format": "mediumThreeByTwo440",
          "height": 293,
          "width": 440,
          "type": "image",
          "subtype": "photo",
          "caption": "The Trump International Hotel in Washington.",
          "copyright": "Gabriella Demczuk for The New York Times"
        }
      ]
    }
"""
代码很好:

if let data = jsonString.data(using: .utf8)
{
    let decoder = JSONDecoder()
    let result = try? decoder.decode(Article.self, from: data)
    print(result)
}
但是,以下JSON字符串被解码为nill:

        let jsonString = """
        {
            "slug_name": "01a3_quote-web",
            "section": "Today’s Paper",
            "subsection": "",
            "title": "Quotation of the Day: Who Killed Atlanta’s Children? Retesting Evidence After 40 Years",
            "abstract": "Quotation of the Day for Wednesday, May 1, 2019.",
            "url": "https://www.nytimes.com/2019/04/30/todayspaper/quotation-of-the-day-who-killed-atlantas-children-retesting-evidence-after-40-years.html",
            "byline": "",
            "thumbnail_standard": "",
            "item_type": "Article",
            "source": "The New York Times",
            "updated_date": "2019-04-30T21:26:36-04:00",
            "created_date": "2019-04-30T21:26:36-04:00",
            "published_date": "2019-04-29T20:00:00-04:00",
            "first_published_date": "2019-04-30T21:25:06-04:00",
            "material_type_facet": "Quote",
            "kicker": null,
            "subheadline": null,
            "des_facet": "",
            "org_facet": "",
            "per_facet": "",
            "geo_facet": "",
            "related_urls": null,
            "multimedia": ""
        }
"""
尽管我将对象中的属性设置为可选,但在decoder.decode方法中使用了.self


如何获取要解码的第二个JSON字符串?

解决方案

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let abstract = try container.decode(String.self, forKey: .abstract)
    let thumbnailStandard = try container.decode(String.self, forKey: .thumbnailStandard)
    var multimedia: [Multimedia] = []
    do {
        multimedia = try container.decode([Multimedia].self, forKey: .multimedia)
    } catch {}
    let title = try container.decode(String.self, forKey: .title)
    let url = try container.decode(URL.self, forKey: .url)

    self.init(abstract: abstract, thumbnailStandard: thumbnailStandard, multimedia: multimedia, title: title, url: url)
}
编辑

试试这个

struct Article {
    let abstract: String
    let thumbnailStandard: String
    let multimedia: [Multimedia]
    let title: String
    let url: URL
}

extension Article: Decodable {
    enum CodingKeys: String, CodingKey {
        case abstract
        case thumbnailStandard = "thumbnail_standard"
        case multimedia
        case title
        case url
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let abstract = try container.decode(String.self, forKey: .abstract)
        let thumbnailStandard = try container.decode(String.self, forKey: .thumbnailStandard)
        let multimedia = try container.decode([Multimedia].self, forKey: .multimedia)
        let title = try container.decode(String.self, forKey: .title)
        let url = try container.decode(URL.self, forKey: .url)

        self.init(abstract: abstract, thumbnailStandard: thumbnailStandard, multimedia: multimedia, title: title, url: url)
    }
}

struct Multimedia: Codable {
    let url: String
}

参考:

好吧,这种不一致性应该由api来处理。但通过引入
enum
,您可以优雅地处理不同类型的返回类型,如下所示

enum MultiMediaType: Codable {

    case string(String)
    case array(Array<Multimedia>)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            self = .array(try container.decode([Multimedia].self))
        } catch DecodingError.typeMismatch {
            self = .string(try container.decode(String.self))
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .string(let value):
            try container.encode(value)
        case .array(let value):
            try container.encode(value)
        }
    }
}

public struct Article : Codable {
    var abstract: String?
    var thumbnail_standard: String?
    var multimedia: MultiMediaType
    var title: String?
    var url: URL?
}
枚举多媒体类型:可编码{ 大小写字符串(字符串) 案例数组(数组) init(来自解码器:解码器)抛出{ let container=尝试解码器。singleValueContainer() 做{ self=.array(尝试container.decode([Multimedia].self)) }捕获解码错误。类型不匹配{ self=.string(尝试container.decode(string.self)) } } func encode(到编码器:编码器)抛出{ var container=encoder.singleValueContainer() 切换自身{ case.string(let值): 尝试container.encode(值) case.array(let值): 尝试container.encode(值) } } } 公共结构文章:可编码{ var摘要:字符串? var-u标准:字符串? 多媒体:多媒体类型 变量标题:字符串? var-url:url? }
解决方案是手动解码所有值可以是不同类型的键

在此示例中,
multimedia
是可选的(
[multimedia]
nil
),而
perFacet
是非可选的
[String]
,如果值为空字符串,则为空

所有结构成员都是常量(
let
),并且添加了
convertFromSnakeCase
策略以去除蛇壳名称



如果多媒体仍返回零,则应返回
[]
而不是
是,我们可以写信给《纽约时报》投诉。或者,可能接受它返回“”并创建一个解决方案。我没有使用它,但一个可能的解决方案是使用AnyCodable库()并将
多媒体
字段定义为
AnyDecodable
。这需要一些额外的工作才能正确提取多媒体数据(即转换为
[multimedia]
,如果转换失败,则您知道它是空的或“”),但其余的转换应在任何一种情况下都能工作。这与实现的解决方案最接近。
struct Article : Decodable {
    let abstract: String
    let thumbnailStandard: String
    let multimedia: [Multimedia]?
    let perFacet : [String]
    let title: String
    let url: URL

    private enum CodingKeys: String, CodingKey {
        case abstract, multimedia, thumbnailStandard, title, url, perFacet
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        abstract = try container.decode(String.self, forKey: .abstract)
        thumbnailStandard = try container.decode(String.self, forKey: .thumbnailStandard)
        do {
            perFacet = try container.decode([String].self, forKey: .perFacet)
        } catch DecodingError.typeMismatch {
            perFacet = []
        }
        do {
            multimedia = try container.decode([Multimedia].self, forKey: .multimedia)
        } catch DecodingError.typeMismatch {
            multimedia = nil
        }
        title = try container.decode(String.self, forKey: .title)
        url = try container.decode(URL.self, forKey: .url)
    }
}

struct Multimedia: Decodable {
    let url: URL
    let format, type, subtype, caption, copyright: String
    let height, width: Int
}
let data = Data(jsonString.utf8)

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
    let result = try decoder.decode(Article.self, from: data)
    print(result)
} catch { print(error) }