Swift 在 Json 中解码嵌套数组时没有数据

No Data when Decoding Nested Array in Json by Swift

我想解码此数据并想在 UI 中单独显示字段。 Json 我从 API

收到的数据
{
"IsSuccess": true,
"Message": "Data Returned",
"ResponseData": [
    {
        "PackageId": 1025,
        "PackageName": "17 OH Progesterone",
        "Price": 0.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [
            {
                "TestSampleTypeId": "50",
                "SampleName": "Serum",
                "ColourCode": "#FFB500"
            }
        ]
    },
    {
        "PackageId": 1916,
        "PackageName": "24 hour Albumin creatinine ratio (ACR)",
        "Price": 120.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [
            {
                "TestSampleTypeId": "66",
                "SampleName": "24 hrs Urine",
                "ColourCode": "#212DC1"
            }
        ]
    },
    {
        "PackageId": 1914,
        "PackageName": "24 Hour Microalbumin Creatinine Ratio",
        "Price": 110.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [
            {
                "TestSampleTypeId": "66",
                "SampleName": "24 hrs Urine",
                "ColourCode": "#212DC1"
            }
        ]
    },
    {
        "PackageId": 1913,
        "PackageName": "24 Hours Protein Creatinine Ratio (PCR) ",
        "Price": 12.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [
            {
                "TestSampleTypeId": "66",
                "SampleName": "24 hrs Urine",
                "ColourCode": "#212DC1"
            }
        ]
    },
    {
        "PackageId": 936,
        "PackageName": "24 Hours Urinary Phosphorous",
        "Price": 15.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [
            {
                "TestSampleTypeId": "66",
                "SampleName": "24 hrs Urine",
                "ColourCode": "#212DC1"
            }
        ]
    },
    {
        "PackageId": 937,
        "PackageName": "24 Hours Urinary Potassium",
        "Price": 2.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [
            {
                "TestSampleTypeId": "66",
                "SampleName": "24 hrs Urine",
                "ColourCode": "#212DC1"
            }
        ]
    },
   ......
   ]}

上面的解码模型

import Foundation


          struct PriceList {
                           let Success: Bool
                           let message: String
                           let Response: [ResponseList]?
                           }

         extension PriceList:Codable
                    {
                   enum CodingKeys: String, CodingKey 
                          {
                          case Success = "IsSuccess"
                          case message = "Message"
                          case Response = "ResponseData"
                         }

        init(from decoder:Decoder) throws {
  
                  let container = try decoder.container(keyedBy:CodingKeys.self)
                  Success = try container.decode(Bool.self,forKey: .Success)    
                  message = try container.decode(String.self,forKey: .message)
                  Response = try container.decode([ResponseList].self,forKey: .Response)
                          }
                       }


                struct ResponseList 
                         {
                             let packageID: Int
                             let packageName: String
                             let price, discountedPrice: Double
                             let type: String
                             let testPackageGroupID: Int
                             let SampleType: [SampleTypeList]?
                         }
               extension ResponseList:Decodable
                         {
                      enum CodingKeys: String, CodingKey {
                             case packageID = "PackageId"
                             case packageName = "PackageName"
                             case price = "Price"
                             case discountedPrice = "DiscountedPrice"
                             case type = "Type"
                             case testPackageGroupID = "TestPackageGroupId"
                             case SampleType= "SampleTypeList"
                         }

              init(from decoder:Decoder) throws
                             {
  
                       let container = try decoder.container(keyedBy:CodingKeys.self)
                       packageID = try container.decode(String.self,forKey: .packageID)    
                       packageName= try container.decode(String.self,forKey: .packageName)
                       price= try container.decode(Double.self,forKey: .price)
                  discountedPrice= try container.decode(Double.self,forKey:.discountedPrice)
                     type= try container.decode(String.self,forKey: .type)
          testPackageGroupID = try container.decode(String.self,forKey:  .testPackageGroupID )
             SampleType= try container.decode([SampleTypeList].self,forKey: .SampleType) 
                                   }
                        }


               struct SampleTypeList        
                                 {
                          let testSampleTypeID, sampleName, colourCode: String
                                 }
               extension SampleTypeList:Codable {
                          enum SampleKeys: String, CodingKey {
                          case testSampleTypeID = "TestSampleTypeId"
                          case sampleName = "SampleName"
                          case colourCode = "ColourCode"
                                 }
               init(from decoder:Decoder) throws 
                         {
                          let container = try decoder.container(keyedBy:SampleKeys.self)
          testSampleTypeID = try container.decode(String.self,forKey: .testSampleTypeID )    
                      sampleName = try container.decode(String.self,forKey: .sampleName )
                      colourCode = try container.decode(String.self,forKey: .colourCode)
                         }
                       }

这是在 playground 中编写的代码:

 var urlComponents = URLComponents()
urlComponents.scheme = "http"
urlComponents.host = "xx.xx.xxx.x"
urlComponents.path = "/api/test/home"
urlComponents.queryItems = [URLQueryItem(name: "pricelistGroupId",value: "12")]

let url = urlComponents.url

var request = URLRequest(url: url!)


request.addValue("application/json", forHTTPHeaderField: 
"Accept")
request.addValue("Basic \(authToken)", forHTTPHeaderField: 
 "Authorization")


let task = URLSession.shared.dataTask(with: request)
{
    (data, response, error) in
   
    if let error = error 
    {
    print("Error \(error)")
    return
    }
    if let response = response as? HTTPURLResponse {
    print("Response \(response.statusCode)")
   
    }
   if let data = data
 {

  let dataString = String(data:data, encoding: .utf8)
  print(dataString)
  let json = try? JSONDecoder().decode(PriceList.self,from:data)
 print(json)
}
}

print(dataString) 正在打印数据。但是,没有 print(json) 的数据,它在操场上显示为 nil。

ResponseList init 卡在 259 次(右侧 playground 选项卡显示所有进程)而 SampleTypeList 卡在 346 次。

如果我删除 ? (可选)来自 [ResponseList]?和 [样本类型列表]?它显示“无法获取未加密的解码容器——而是找到了空值。”

请忽略拼写错误。

程序卡在它为例如两个实例主要找到 null 的地方

SampleTypeList = null(在JSON中多次出现)testPackageGroupID = null

试试这个,对我有用:

extension ResponseList: Codable {  // <-- here not Decodable
    
    enum CodingKeys: String, CodingKey {
        case packageID = "PackageId"
        case packageName = "PackageName"
        case price = "Price"
        case discountedPrice = "DiscountedPrice"
        case type = "Type"
        case testPackageGroupID = "TestPackageGroupId"
        case SampleType = "SampleTypeList"
    }
    
    init(from decoder:Decoder) throws {
        let container = try decoder.container(keyedBy:CodingKeys.self)
        packageID = try container.decode(Int.self,forKey: .packageID)  // <-- here Int
        packageName = try container.decode(String.self,forKey: .packageName)
        price = try container.decode(Double.self,forKey: .price)
        discountedPrice = try container.decode(Double.self,forKey:.discountedPrice)
        type = try container.decode(String.self,forKey: .type)
        testPackageGroupID = try container.decode(Int.self,forKey: .testPackageGroupID ) // <-- here Int
        SampleType = try container.decode([SampleTypeList].self,forKey: .SampleType)
    }
}

编辑:

这是我用来显示解码给定 json 数据与我的答案相符的代码。

import SwiftUI
import Foundation

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct PriceList {
    let Success: Bool
    let message: String
    let Response: [ResponseList]?
}

extension PriceList: Codable {
    
    enum CodingKeys: String, CodingKey {
        case Success = "IsSuccess"
        case message = "Message"
        case Response = "ResponseData"
    }
    
    init(from decoder:Decoder) throws {
        let container = try decoder.container(keyedBy:CodingKeys.self)
        Success = try container.decode(Bool.self,forKey: .Success)
        message = try container.decode(String.self,forKey: .message)
        Response = try container.decode([ResponseList].self,forKey: .Response)
    }
}

struct ResponseList {
    let packageID: Int
    let packageName: String
    let price, discountedPrice: Double
    let type: String
    let testPackageGroupID: Int
    let SampleType: [SampleTypeList]?
}

extension ResponseList: Codable {  // <-- here not Decodable
    
    enum CodingKeys: String, CodingKey {
        case packageID = "PackageId"
        case packageName = "PackageName"
        case price = "Price"
        case discountedPrice = "DiscountedPrice"
        case type = "Type"
        case testPackageGroupID = "TestPackageGroupId"
        case SampleType = "SampleTypeList"
    }
    
    init(from decoder:Decoder) throws {
        let container = try decoder.container(keyedBy:CodingKeys.self)
        packageID = try container.decode(Int.self,forKey: .packageID)  // <-- here Int
        packageName = try container.decode(String.self,forKey: .packageName)
        price = try container.decode(Double.self,forKey: .price)
        discountedPrice = try container.decode(Double.self,forKey:.discountedPrice)
        type = try container.decode(String.self,forKey: .type)
        testPackageGroupID = try container.decode(Int.self,forKey: .testPackageGroupID ) // <-- here Int
        SampleType = try container.decode([SampleTypeList].self,forKey: .SampleType)
    }
}

struct SampleTypeList {
    let testSampleTypeID, sampleName, colourCode: String
}

extension SampleTypeList:Codable {
    
    enum SampleKeys: String, CodingKey {
        case testSampleTypeID = "TestSampleTypeId"
        case sampleName = "SampleName"
        case colourCode = "ColourCode"
    }
    
    init(from decoder:Decoder) throws {
        let container = try decoder.container(keyedBy:SampleKeys.self)
        testSampleTypeID = try container.decode(String.self,forKey: .testSampleTypeID )
        sampleName = try container.decode(String.self,forKey: .sampleName )
        colourCode = try container.decode(String.self,forKey: .colourCode)
    }
}

struct ContentView: View {
    
    @State var priceList: PriceList?
    
    var body: some View {
        Text(priceList?.message ?? "no data")
            .onAppear {
                let jsonString  = """
                {
                "IsSuccess": true,
                "Message": "Data Returned",
                "ResponseData": [
                    {
                        "PackageId": 1025,
                        "PackageName": "17 OH Progesterone",
                        "Price": 0.00,
                        "DiscountedPrice": 1.0,
                        "Type": "Test",
                        "TestPackageGroupId": 3,
                        "SampleTypeList": [
                            {
                                "TestSampleTypeId": "50",
                                "SampleName": "Serum",
                                "ColourCode": "#FFB500"
                            }
                        ]
                    },
                    {
                        "PackageId": 1916,
                        "PackageName": "24 hour Albumin creatinine ratio (ACR)",
                        "Price": 120.00,
                        "DiscountedPrice": 1.0,
                        "Type": "Test",
                        "TestPackageGroupId": 3,
                        "SampleTypeList": [
                            {
                                "TestSampleTypeId": "66",
                                "SampleName": "24 hrs Urine",
                                "ColourCode": "#212DC1"
                            }
                        ]
                    },
                    {
                        "PackageId": 1914,
                        "PackageName": "24 Hour Microalbumin Creatinine Ratio",
                        "Price": 110.00,
                        "DiscountedPrice": 1.0,
                        "Type": "Test",
                        "TestPackageGroupId": 3,
                        "SampleTypeList": [
                            {
                                "TestSampleTypeId": "66",
                                "SampleName": "24 hrs Urine",
                                "ColourCode": "#212DC1"
                            }
                        ]
                    },
                    {
                        "PackageId": 1913,
                        "PackageName": "24 Hours Protein Creatinine Ratio (PCR) ",
                        "Price": 12.00,
                        "DiscountedPrice": 1.0,
                        "Type": "Test",
                        "TestPackageGroupId": 3,
                        "SampleTypeList": [
                            {
                                "TestSampleTypeId": "66",
                                "SampleName": "24 hrs Urine",
                                "ColourCode": "#212DC1"
                            }
                        ]
                    },
                    {
                        "PackageId": 936,
                        "PackageName": "24 Hours Urinary Phosphorous",
                        "Price": 15.00,
                        "DiscountedPrice": 1.0,
                        "Type": "Test",
                        "TestPackageGroupId": 3,
                        "SampleTypeList": [
                            {
                                "TestSampleTypeId": "66",
                                "SampleName": "24 hrs Urine",
                                "ColourCode": "#212DC1"
                            }
                        ]
                    },
                    {
                        "PackageId": 937,
                        "PackageName": "24 Hours Urinary Potassium",
                        "Price": 2.00,
                        "DiscountedPrice": 1.0,
                        "Type": "Test",
                        "TestPackageGroupId": 3,
                        "SampleTypeList": [
                            {
                                "TestSampleTypeId": "66",
                                "SampleName": "24 hrs Urine",
                                "ColourCode": "#212DC1"
                            }
                        ]
                    }
                   ]
                }
                """
                let data = jsonString.data(using: .utf8)
                priceList = try? JSONDecoder().decode(PriceList.self, from: data!)
                print("\n--> priceList: \(priceList) \n")
            }
    }
}

编辑-2:

如果您的 json 数据中可以包含此内容:

"TestPackageGroupId": null,
"SampleTypeList": null
                    

然后尝试这种方法来解码您的 json 数据:

struct ResponseList {
    let packageID: Int
    let packageName: String
    let price, discountedPrice: Double
    let type: String
    let testPackageGroupID: Int? // <--- here optional
    let SampleType: [SampleTypeList]?  // <--- here optional
}

extension ResponseList: Codable {  // <-- here not Decodable
    
    enum CodingKeys: String, CodingKey {
        case packageID = "PackageId"
        case packageName = "PackageName"
        case price = "Price"
        case discountedPrice = "DiscountedPrice"
        case type = "Type"
        case testPackageGroupID = "TestPackageGroupId"
        case SampleType = "SampleTypeList"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy:CodingKeys.self)
        packageID = try container.decode(Int.self,forKey: .packageID)  // <-- here Int
        packageName = try container.decode(String.self,forKey: .packageName)
        price = try container.decode(Double.self,forKey: .price)
        discountedPrice = try container.decode(Double.self,forKey:.discountedPrice)
        type = try container.decode(String.self,forKey: .type)
        // --- here
        testPackageGroupID = try container.decodeIfPresent(Int.self,forKey: .testPackageGroupID)
        SampleType = try container.decodeIfPresent([SampleTypeList].self,forKey: .SampleType)
    }

}

对于任何其他可以具有 null.

的元素也是如此

这段代码就够了:

struct PriceList: Decodable {
    let success: Bool
    let message: String
    let response: [ResponseList]

    enum CodingKeys: String, CodingKey {
        case success = "IsSuccess"
        case message = "Message"
        case response = "ResponseData"
    }
}


struct ResponseList: Decodable {
    let packageID: Int
    let packageName: String
    let price, discountedPrice: Double
    let type: String
    let testPackageGroupID: Int?
    let sampleType: [SampleTypeList]?

    enum CodingKeys: String, CodingKey {
        case packageID = "PackageId"
        case packageName = "PackageName"
        case price = "Price"
        case discountedPrice = "DiscountedPrice"
        case type = "Type"
        case testPackageGroupID = "TestPackageGroupId"
        case sampleType = "SampleTypeList"
    }
}


struct SampleTypeList: Decodable {
    let testSampleTypeID: String
    let sampleName: String
    let colourCode: String
    enum CodingKeys: String, CodingKey {
        case testSampleTypeID = "TestSampleTypeId"
        case sampleName = "SampleName"
        case colourCode = "ColourCode"
    }
}

我修复了什么,我不喜欢你的示例代码:

  • 请以小写开头命名您的变量,这是惯例,并且更易于阅读(如果每个人都遵循相同的convention/standards)。
  • 使您的代码可以为我们编译。这并不难,但如果有人想帮助您,我们只 copy/paste 您的代码并测试它而不是修复其中的所有内容会容易得多。您将有更好的机会获得答案。有一个 Cannot assign value of type 'String' to type 'Int' 因为 packageID 被设置为一个 Int 并试图被解码为一个字符串,缺少空格: variable= 而不是 variable = 等。这很烦人让我们修复它以便能够工作。
  • 你打印了JSON,这是一个很好的点,测试一下,不再需要Web API调用,看下面的示例
  • 你说有些值可以为空,所以请提供这些示例的 JSON,如果需要,请剪切,请参阅 JSON 我用作示例,我检查了 JSON 在线验证器是有效的,仅此而已。由于您认为这 2 个值可能为空,因此我使用了所有可能性:一种为 none 空,一种为空,一种为另一个空,一种为空。然后,对于其中的每一个,我都将值作为可选值。
  • 我删除了所有 init(from decoder:),因为它们没用。如果您使用 let container = try decoder.container(keyedBy:CodingKeys.self); variable = try container.decode(VariableType.self,forKey: .correspondingCodingKeyInTheEnumCase) 对每个值进行解码,则该代码在编译时已由 Apple 在内部完成。

有样本测试:

let jsonStr = """
{
"IsSuccess": true,
"Message": "Data Returned",
"ResponseData": [{
        "PackageId": 1025,
        "PackageName": "17 OH Progesterone",
        "Price": 0.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": [{
            "TestSampleTypeId": "50",
            "SampleName": "Serum",
            "ColourCode": "#FFB500"
        }]
    },
    {
        "PackageId": 1916,
        "PackageName": "24 hour Albumin creatinine ratio (ACR)",
        "Price": 120.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": 3,
        "SampleTypeList": null
    },
    {
        "PackageId": 1914,
        "PackageName": "24 Hour Microalbumin Creatinine Ratio",
        "Price": 110.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": null,
        "SampleTypeList": [{
            "TestSampleTypeId": "66",
            "SampleName": "24 hrs Urine",
            "ColourCode": "#212DC1"
        }]
    },
    {
        "PackageId": 1913,
        "PackageName": "24 Hours Protein Creatinine Ratio (PCR) ",
        "Price": 12.00,
        "DiscountedPrice": 1.0,
        "Type": "Test",
        "TestPackageGroupId": null,
        "SampleTypeList": null
    }
]
}
"""

do {
    let priceList = try JSONDecoder().decode(PriceList.self, from: Data(jsonStr.utf8))
    print(priceList)
} catch {
    print("Error while decoding: \(error)")
}