如何将 JSON 解码为不同的类型?

Posted

技术标签:

【中文标题】如何将 JSON 解码为不同的类型?【英文标题】:How to decode JSON Into different types? 【发布时间】:2022-01-07 13:13:58 【问题描述】:

我有这个本地 JSON 文件,其中包含标题、开始和结束。我想从开始键和结束键创建日期,但标题是字符串,所以我可以创建事件对象。现在我已经将所有内容解码为字符串。所以我试图创建一个自定义初始化,但一个错误不断出现“从初始化程序返回而不初始化所有存储的属性”。不知道我做错了什么

这是我的事件模型和我的 JSONFile

struct Event: Decodable & Equatable 
    let title : String
    let start : Date
    let end : Date
    
    //Custom decoding init
    init(from decoder : Decoder) throws 
        
        let container = try decoder.singleValueContainer()
        let stringType = try container.decode(String.self)
        
        switch stringType 
        case "title":
            self.title = try container.decode(String.self)
        case "start":
            self.start = try container.decode(Date.self)
        case "end":
            self.end = try container.decode(Date.self)
        default:
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "Not valid date \(stringType)")
        
    

JSON

 ["title": "Evening Picnic", "start": "November 10, 2018 6:00 PM", "end": "November 10, 2018 7:00 PM", "title": "Nap Break", "start": "November 8, 2018 12:30 PM", "end": "November 8, 2018 1:30 PM", "title": "Football Game", "start": "November 3, 2018 6:00 PM", "end": "November 3, 2018 10:00 PM", "title": "Evening Cookout with Friends", "start": "November 6, 2018 5:00 PM", "end": "November 6, 2018 10:00 PM", "title": "Roller Derby", "start": "November 7, 2018 12:00 PM", "end": "November 7, 2018 2:30 PM", "title": "Basketball Game", "start": "November 8, 2018 7:30 PM", "end": "November 8, 2018 10:30 PM", "title": "Local Pub with Friends", "start": "November 1, 2018 7:30 PM", "end": "November 1, 2018 11:00 PM", "title": "Dentist Appointment", "start": "November 10, 2018 1:45 PM", "end": "November 10, 2018 2:30 PM", "title": "Free Donuts", "start": "November 9, 2018 3:00 PM", "end": "November 9, 2018 4:00 PM", "title": "TV Show Marathon", "start": "November 9, 2018 4:30 PM", "end": "November 9, 2018 9:00 PM", "title": "Lunch with Friends", "start": "November 8, 2018 11:30 AM", "end": "November 8, 2018 1:00 PM", "title": "SF Coffee Festival","start": "November 6, 2018 6:00 PM","end": "November 6, 2018 9:00 PM", "title": "Beer with Friends", "start": "November 9, 2018 8:00 PM", "end": "November 9, 2018 9:30 PM", "title": "Yoga", "start": "November 1, 2018 6:00 PM", "end": "November 1, 2018 7:30 PM", "title": "Rock Concert", "start": "November 7, 2018 6:30 PM", "end": "November 7, 2018 11:00 PM", "title": "Lunch Meeting", "start": "November 9, 2018 12:30 PM", "end": "November 9, 2018 2:30 PM", "title": "Bicycling with Friends", "start": "November 1, 2018 6:00 AM", "end": "November 1, 2018 9:30 AM", "title": "Birthday Party", "start": "November 10, 2018 12:30 PM", "end": "November 10, 2018 8:30 PM", "title": "Football Tailgate with John", "start": "November 3, 2018 6:00 PM", "end": "November 3, 2018 10:00 PM"]

【问题讨论】:

参见JSONDecoder.dateDecodingStrategy,或编写自定义解码初始化。不需要第二个结构。无论如何,这种日期格式将很难解码。 我已经在你最后的question中展示了如何将它们解码为日期 我正在尝试创建自定义解码初始化,但似乎无法正确完成。 找一个例子。您的代码甚至与自定义解码 init 的样子都不相近。 我已经再次展示了如何解码您的日期。您其他问题中链接的重复帖子具有完全相同的日期格式。 【参考方案1】:

没有不同的类型,所有字典中的所有值始终具有相同的类型String

因此singleValueContainer 是错误的方法,根本不需要自定义初始化程序。

要将字符串日期解码为Date,只需添加适当的日期解码策略

let jsonString = """
 ["title": "Evening Picnic", "start": "November 10, 2018 6:00 PM", "end": "November 10, 2018 7:00 PM", "title": "Nap Break", "start": "November 8, 2018 12:30 PM", "end": "November 8, 2018 1:30 PM", "title": "Football Game", "start": "November 3, 2018 6:00 PM", "end": "November 3, 2018 10:00 PM", "title": "Evening Cookout with Friends", "start": "November 6, 2018 5:00 PM", "end": "November 6, 2018 10:00 PM", "title": "Roller Derby", "start": "November 7, 2018 12:00 PM", "end": "November 7, 2018 2:30 PM", "title": "Basketball Game", "start": "November 8, 2018 7:30 PM", "end": "November 8, 2018 10:30 PM", "title": "Local Pub with Friends", "start": "November 1, 2018 7:30 PM", "end": "November 1, 2018 11:00 PM", "title": "Dentist Appointment", "start": "November 10, 2018 1:45 PM", "end": "November 10, 2018 2:30 PM", "title": "Free Donuts", "start": "November 9, 2018 3:00 PM", "end": "November 9, 2018 4:00 PM", "title": "TV Show Marathon", "start": "November 9, 2018 4:30 PM", "end": "November 9, 2018 9:00 PM", "title": "Lunch with Friends", "start": "November 8, 2018 11:30 AM", "end": "November 8, 2018 1:00 PM", "title": "SF Coffee Festival","start": "November 6, 2018 6:00 PM","end": "November 6, 2018 9:00 PM", "title": "Beer with Friends", "start": "November 9, 2018 8:00 PM", "end": "November 9, 2018 9:30 PM", "title": "Yoga", "start": "November 1, 2018 6:00 PM", "end": "November 1, 2018 7:30 PM", "title": "Rock Concert", "start": "November 7, 2018 6:30 PM", "end": "November 7, 2018 11:00 PM", "title": "Lunch Meeting", "start": "November 9, 2018 12:30 PM", "end": "November 9, 2018 2:30 PM", "title": "Bicycling with Friends", "start": "November 1, 2018 6:00 AM", "end": "November 1, 2018 9:30 AM", "title": "Birthday Party", "start": "November 10, 2018 12:30 PM", "end": "November 10, 2018 8:30 PM", "title": "Football Tailgate with John", "start": "November 3, 2018 6:00 PM", "end": "November 3, 2018 10:00 PM"]
"""

struct Event : Decodable 
    let title : String
    let start, end : Date


let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "MMMM dd, yyyy h:mm a"

do 
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .formatted(formatter)
    let result = try decoder.decode([Event].self, from: Data(jsonString.utf8))
    print(result)
 catch 
    print(error)

【讨论】:

太棒了!我确实尝试过它并且它工作,在相当新的 swift 中,所以其中一些概念对我来说仍然很难掌握。不过非常感谢!说真的,我无法表达我的感激之情!【参考方案2】:

-> 首先你需要接受正确的 JSON 数据

let json = """

      "data" : ["title": "Evening Picnic", "start": "November 10, 2018 6:00 PM", "end": "November 10, 2018 7:00 PM", "title": "Nap Break", "start": "November 8, 2018 12:30 PM", "end": "November 8, 2018 1:30 PM", "title": "Football Game", "start": "November 3, 2018 6:00 PM", "end": "November 3, 2018 10:00 PM", "title": "Evening Cookout with Friends", "start": "November 6, 2018 5:00 PM", "end": "November 6, 2018 10:00 PM", "title": "Roller Derby", "start": "November 7, 2018 12:00 PM", "end": "November 7, 2018 2:30 PM", "title": "Basketball Game", "start": "November 8, 2018 7:30 PM", "end": "November 8, 2018 10:30 PM", "title": "Local Pub with Friends", "start": "November 1, 2018 7:30 PM", "end": "November 1, 2018 11:00 PM", "title": "Dentist Appointment", "start": "November 10, 2018 1:45 PM", "end": "November 10, 2018 2:30 PM", "title": "Free Donuts", "start": "November 9, 2018 3:00 PM", "end": "November 9, 2018 4:00 PM", "title": "TV Show Marathon", "start": "November 9, 2018 4:30 PM", "end": "November 9, 2018 9:00 PM", "title": "Lunch with Friends", "start": "November 8, 2018 11:30 AM", "end": "November 8, 2018 1:00 PM", "title": "SF Coffee Festival","start": "November 6, 2018 6:00 PM","end": "November 6, 2018 9:00 PM", "title": "Beer with Friends", "start": "November 9, 2018 8:00 PM", "end": "November 9, 2018 9:30 PM", "title": "Yoga", "start": "November 1, 2018 6:00 PM", "end": "November 1, 2018 7:30 PM", "title": "Rock Concert", "start": "November 7, 2018 6:30 PM", "end": "November 7, 2018 11:00 PM", "title": "Lunch Meeting", "start": "November 9, 2018 12:30 PM", "end": "November 9, 2018 2:30 PM", "title": "Bicycling with Friends", "start": "November 1, 2018 6:00 AM", "end": "November 1, 2018 9:30 AM", "title": "Birthday Party", "start": "November 10, 2018 12:30 PM", "end": "November 10, 2018 8:30 PM", "title": "Football Tailgate with John", "start": "November 3, 2018 6:00 PM", "end": "November 3, 2018 10:00 PM"]

"""

-> 那么你需要创建一个可编码的类或结构

struct Main: Codable 
    var data: [MYStruct]?


// MARK: - Datum
struct MYStruct: Codable 
    var title, start, end: String?

-> 如果键 "start": "November 3, 2018 6:00 PM" 这里的 "start" 延迟你可以像这样使用codingKey

struct MYStruct: Codable 
    var title, start, end: String?
    enum CodingKeys: String, CodingKey 
        case title = "title" // key name as string 
        case startDate = "start"
        case endDate = "end"

-> 然后创建一个 JSON 解码器来解码 jsonData

override func viewDidLoad() 
    super.viewDidLoad()

    let jsonData = Data(json.utf8)
    let decoder = JSONDecoder()
    let loadedData = try? decoder.decode(Main.self, from: jsonData) // 1st parameter object name 2nd parameter JsonData

    print(loadedData?.data?[0].title)
    
 

-> 请记住,您的 json 始终是正确的策略,并以

结尾

-> 总是应该在密钥对值中

-> 输出

Optional("Evening Picnic")

【讨论】:

以上是关于如何将 JSON 解码为不同的类型?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用解码器将给定 JSON 中 Double 类型的 Codable 属性转换为 Date?

将 JSON 文件解码为不同的目标数组

Swift:如何将可编码类型作为函数输入传递给编码或解码 json

JSON解码器无法将对象解码为对象

JSON 可解码嵌套数组具有不同的格式和类型

您可以将子 JSON 对象解码为其字符串表示形式而不是具体类型吗?