後端回傳 JSON 中 responseinfo 欄位為**雙重編碼的 JSON 字串** (字串內包含轉義引號的 JSON 陣列)。Swift 需兩次解碼:先解外層,再將 responseinfo 字串轉 Data 再解內層。
{
"responsestatus": 1,
"responsecode": "2002",
"responseinfo": "[{\"USERID\":\"18265309900\",\"USERNAME\":\"麻雀貓\",...}, {\"USERID\":\"18505237553\",\"USERNAME\":\"喵大咪\",...}]"
}
struct APIResponse: Codable {
let responsestatus: Int
let responsecode: String
let responseinfo: String // 先解為 String
}
struct User: Codable {
let USERID: String
let USERNAME: String
let PASSWORD: String
let PHONE: String
let EMAIL: String
let REMARK: String
let STATUS: String
let CMPID: String
let MCMPID: String
let OPTID: String
let TRNDATE: String
let CREATEID: String
let CREATEDATE: String
let LOGINDATE: String
let IMGSRC: String
}
func parseUsers(from data: Data) throws -> [User] {
// 1. 解析外層
let outer = try JSONDecoder().decode(APIResponse.self, from: data)
// 2. 將 responseinfo 字串轉 Data
guard let innerData = outer.responseinfo.data(using: .utf8) else {
throw ParseError.invalidResponseInfo
}
// 3. 解析內層陣列
let users = try JSONDecoder().decode([User].self, from: innerData)
return users
}
// 使用
do {
let users = try parseUsers(from: responseData)
print("解析到 \(users.count) 位用戶")
users.forEach { print($0.USERNAME) }
} catch {
print("解析失敗: \(error)")
}
responseinfo 在外層模型中定義為 String,而非 [User] 或 Any。.data(using: .utf8) 將字串轉 Data 再送入 JSONDecoder。[User].self。let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase // 若欄位為 snake_case
decoder.dateDecodingStrategy = .formatted(dateFormatter) // 統一日期格式
| 錯誤 | 原因 | 修正 |
|---|---|---|
dataCorrupted | 字串含非法轉義/編碼非 UTF-8 | 確認後端輸出標準 JSON,必要時 .replacingOccurrences(of: "\\", with: "") |
keyNotFound | 模型屬性名與 JSON key 大小寫不符 | 加 CodingKeys 對應或用 .convertFromSnakeCase |
typeMismatch | 數值型欄位實為字串 | 模型統一用 String 再轉型,或用 JSONDecoder 的 nonConformingFloatDecodingStrategy |
提問者最終回饋:「用自己的方法搞定了,看來是沒靜下心來認真看一看」。雙重解碼是處理「字串化 JSON」欄位的標準模式,熟記即可。