在swift iOS中使用JSON解码器解析本地Json

Posted

技术标签:

【中文标题】在swift iOS中使用JSON解码器解析本地Json【英文标题】:Local Json parsing with JSON Decoder in swift iOS 【发布时间】:2021-12-22 15:45:27 【问题描述】:

我想解析本地 JSON 并使用 JSON 解码器访问内部内容。我是 JSON 解码器的新手,任何人都可以建议。

JSON:

[
  
    "bookmark_intro": 
      "title": "What's new in bookmarks",
      "enabled": "yes",
      "content": [
        
          "subtitle": "Organize with folders",
          "content": "Organize your bookmarks in folders for quick and easy access.",
          "icon": "image1.png"
        ,
        
          "subtitle": "Share and subscribe",
          "content": "Share your folders with your colleagues and subscribe to their folders to keep you informed about updates.",
          "icon": "image2.png"
        ,
        
          "subtitle": "And lots more!",
          "content": "Edit bookmarks easier, add bookmarks to multiple folders - all that even offline and synced across all your apps and devices.",
          "icon": "image3.png"
        
      ]
    
  
]

创建模型如下:

struct PremiumTutorialModel : Codable 
    let bookmark_intro : Bookmark_intro?
    enum CodingKeys: String, CodingKey 
        case bookmark_intro = "bookmark_intro"
    
    init(from decoder: Decoder) throws 
        let values = try decoder.container(keyedBy: CodingKeys.self)
        bookmark_intro = try values.decodeIfPresent(Bookmark_intro.self, forKey: .bookmark_intro)
    


struct Bookmark_intro : Codable 
    let title : String?
    let enabled : String?
    let content : [Content]?
    enum CodingKeys: String, CodingKey 
        case title = "title"
        case enabled = "enabled"
        case content = "content"
    
    init(from decoder: Decoder) throws 
        let values = try decoder.container(keyedBy: CodingKeys.self)
        title = try values.decodeIfPresent(String.self, forKey: .title)
        enabled = try values.decodeIfPresent(String.self, forKey: .enabled)
        content = try values.decodeIfPresent([Content].self, forKey: .content)
    


struct Content : Codable 
    let subtitle : String?
    let content : String?
    let icon : String?
    enum CodingKeys: String, CodingKey 
        case subtitle = "subtitle"
        case content = "content"
        case icon = "icon"
    
    init(from decoder: Decoder) throws 
        let values = try decoder.container(keyedBy: CodingKeys.self)
        subtitle = try values.decodeIfPresent(String.self, forKey: .subtitle)
        content = try values.decodeIfPresent(String.self, forKey: .content)
        icon = try values.decodeIfPresent(String.self, forKey: .icon)
    

我试图使用这个函数解析和访问数据,它没有返回模型上的完整数据,任何人都可以提出正确的方法。

   func loadJson(fileName: String) -> PremiumTutorialModel? 
        let decoder = JSONDecoder()
        guard
            let url = Bundle.main.url(forResource: fileName, withExtension: "json"),
            let data = try? Data(contentsOf: url),
            let model = try? decoder.decode(PremiumTutorialModel.self, from: data)
        else 
            return nil
        
        return model
    

谁能建议使用 JSON 解码器解析 json 的正确方法。

【问题讨论】:

对于初学者来说,你的 json 中最外层的类型是一个数组,所以它应该是 decoder.decode([PremiumTutorialModel].self...,你不应该为你自己的本地 json 自定义任何 init(from:),改为更改 json 文件并仅在需要时使您的属性可选。此外,在开发和测试时,您应该在解码时使用适当的错误处理。错误消息通常很有帮助。 【参考方案1】:

您可以创建这些模型:

import Foundation

// MARK: - PremiumTutorialModelElement
struct PremiumTutorialModelElement: Codable 
    let bookmarkIntro: BookmarkIntro

    enum CodingKeys: String, CodingKey 
        case bookmarkIntro = "bookmark_intro"
    


// MARK: - BookmarkIntro
struct BookmarkIntro: Codable 
    let title: String
    let enabled: String
    let content: [Content]

    enum CodingKeys: String, CodingKey 
        case title = "title"
        case enabled = "enabled"
        case content = "content"
    


// MARK: - Content
struct Content: Codable 
    let subtitle: String
    let content: String
    let icon: String

    enum CodingKeys: String, CodingKey 
        case subtitle = "subtitle"
        case content = "content"
        case icon = "icon"
    


typealias PremiumTutorialModel = [PremiumTutorialModelElement]

这个website 非常适合生成 JSON -> Swift 模型。

编辑:

正如@Sulthan 指出的那样,在这种情况下,CodingKeys 是多余的。为了清楚起见,我将它们包括在内,以便其他人更容易修改,但这也可以:

import Foundation

// MARK: - PremiumTutorialModelElement
struct PremiumTutorialModelElement: Codable 
    let bookmarkIntro: BookmarkIntro

    enum CodingKeys: String, CodingKey 
        case bookmarkIntro = "bookmark_intro"
    


// MARK: - BookmarkIntro
struct BookmarkIntro: Codable 
    let title, enabled: String
    let content: [Content]


// MARK: - Content
struct Content: Codable 
    let subtitle, content, icon: String


typealias PremiumTutorialModel = [PremiumTutorialModelElement]

【讨论】:

CodingKeys 在这种情况下不是必需的。 @Sulthan True。在这种情况下,它们对于结构是多余的。为了清晰起见,我更喜欢添加它们,这样其他人可以更轻松地根据自己的需要调整代码。 至少,您可以删除分配并仅将其保留为case subtitle 而不是case subtitle = "subtitle" @Sulthan 再次,真的。再一次,我认为其他人在看到这一点时更容易根据自己的需要进行修改。如果 JSON 中的名称是 "subtitle",但您想调用属性 description,该怎么办?那么很高兴有这个作为模板。 其实,没有。添加不必要的代码意味着 1/ 更难阅读代码 2/ 增加错误的机会。 3/ 编写最初不需要的代码的必要时间。需要时添加代码。【参考方案2】:

我创建了一个可以访问 json 的方法,如下所示。

    func loadJson(fileName: String) -> PremiumTutorialModel? 
    let decoder = JSONDecoder()
    guard
        let url = Bundle.main.url(forResource: fileName, withExtension: "json"),
        let data = try? Data(contentsOf: url),
        let model = try? decoder.decode([PremiumTutorialModel].self, from: data)
    else 
        return nil
    
    return model[0]

【讨论】:

以上是关于在swift iOS中使用JSON解码器解析本地Json的主要内容,如果未能解决你的问题,请参考以下文章

我如何使用解码器在swift 4中解析tableview中的json数组

在 Swift 中使用 JSON 解码器难以解析 JSON 中的整数值

如何在 swift iOS 中使用 swift 高阶函数从本地 json 创建 ViewModel

JSON 的 Swift 可解码解析部分

iOS Swift:使用 AFNetworking 解析响应 json

Swift - JSON解码返回空数组