如何使用嵌套 json 构建结构?

How do I make a struct with nested json?

我的 api 收到 JSON 回复,returns 这个:

[
    {
        "id": 1,
        "chapter": 5,
        "amount": 28,
        "texts": [
            {
                "lyric": "lorem ipsum",
                "number": 1
            },
            {
                "lyric": "lorem foo bar",
                "number": 2
            }
        ],
        "book": 1
    }
]

我试过了

struct Chapter: Decodable, Identifiable {
    var id: Int
    var chapter: Int
    var amount: Int

    struct Lyrics: Codable {
         var lyricText: String
         var lyricNumber: Int
     }

    enum Codingkeys: String, CodingKey {
        case lyricText = "lyric"
        case lyricNumber = "number"
    }
}

但是我在拨打电话时收到以下错误


dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))

我的 API 调用如下所示:

...
    @Published var chapters = [Chapter]()
    func fetchBookDetails() {
        if let url = URL(string: url) {
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url) { (data, response, error) in
                if error == nil {
                    if let safeData = data {
                        do {
                            
                            let response = try JSONDecoder().decode([Chapter].self, from: safeData)
                            DispatchQueue.main.async {
                                self.chapters = response
                            }
                        } catch {
                            print(error)
                        }
                        
                    }
                }
            }
            task.resume()
        }
    }

我猜该结构看起来不错,但是 api 调用有问题 - 知道它可能是什么吗?还是结构做错了

texts是一个子结构(属性数组),所以需要为它定义第二个容器,例如

struct Text: Codable {
    let lyric: String
    let number: Int
}

然后您可以更新 Chapter 以引用子结构,例如...

struct Chapter: Decodable {
    let id: Int
    let chapter: Int
    let amount: Int
    let book: Int
    
    let texts: [Text]
}

最后,加载它...

let chapters = try JSONDecoder().decode([Chapter].self, from: jsonData)

But what about the error message?

dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))

哦,对了,但是错误消息告诉您下载的内容有问题。在这些情况下,我喜欢将数据转换为 String 并在可能的情况下打印出来,这样,您就知道返回给您的是什么。

例如:

let actualText = String(data: safeData, encoding: .utf8)

打印这个看看你实际得到了什么


游乐场测试代码

import UIKit

let jsonText = """
[
    {
        "id": 1,
        "chapter": 5,
        "amount": 28,
        "texts": [
            {
                "lyric": "lorem ipsum",
                "number": 1
            },
            {
                "lyric": "lorem foo bar",
                "number": 2
            },
        ],
        "book": 1
    }
]
"""

struct Text: Codable {
    let lyric: String
    let number: Int
}

struct Chapter: Decodable {
    let id: Int
    let chapter: Int
    let amount: Int
    let book: Int
    
    let texts: [Text]
}

let jsonData = jsonText.data(using: .utf8)!
do {
    let chapters = try JSONDecoder().decode([Chapter].self, from: jsonData)
} catch let error {
    error
}