如何在Swift中创建结构体/类解码指定JSON并提取report节点数据
Hey there! Let's tackle your two questions about decoding JSON in Swift—first the general approach, then the specific structure you provided with a focus on the report node.
Decoding JSON in Swift is straightforward thanks to the Codable protocol (which combines Decodable for reading JSON and Encodable for writing it). Here's the step-by-step process:
- Map JSON structure to Swift types: For every nested object in your JSON, create a corresponding struct (structs are preferred for value types like this) that conforms to
Codable. - Match property names: Ensure your struct's property names exactly match the JSON keys. If they don't align, use a
CodingKeysenum to map them explicitly. - Use
JSONDecoder: Convert your JSON data into Swift objects using the built-inJSONDecoderclass.
Example for a simple JSON:
{"name": "Alice", "age": 30}
Corresponding struct and decoding code:
struct User: Codable { let name: String let age: Int } let jsonData = """ {"name": "Alice", "age": 30} """.data(using: .utf8)! do { let user = try JSONDecoder().decode(User.self, from: jsonData) print(user.name) // Output: Alice } catch { print("Decoding error: \(error)") }
report Node) First, let's revisit your JSON structure:
{"earliest": "2020-10-17", "latest": "2020-10-28", "report": {"purchase": {"total": 1458600.0, "average": 121550.0, "min": 600000.0, "max": 1600.0}, "sale": {"total": 434250.0, "average": 144750.0, "min": 360000.0, "max": 29250.0}, "production": {"total": 792030, "average": 20308.46153846154, "min": 12000, "max": 29700}}}
Notice that purchase, sale, and production share the exact same structure (total, average, min, max). We can reuse a single struct for these instead of writing duplicate code. Here's how to set up your types:
// Reusable struct for purchase/sale/production metrics struct Metric: Codable { let total: Double let average: Double let min: Double let max: Double } // Struct representing the full report node struct Report: Codable { let purchase: Metric let sale: Metric let production: Metric } // Top-level struct to wrap the entire JSON response struct ReportResponse: Codable { let earliest: String let latest: String let report: Report }
Decoding and Extracting the report Node
Now, decode the JSON and pull out the report data (the core part you care about):
let jsonString = """ {"earliest": "2020-10-17", "latest": "2020-10-28", "report": {"purchase": {"total": 1458600.0, "average": 121550.0, "min": 600000.0, "max": 1600.0}, "sale": {"total": 434250.0, "average": 144750.0, "min": 360000.0, "max": 29250.0}, "production": {"total": 792030, "average": 20308.46153846154, "min": 12000, "max": 29700}}} """ // Convert JSON string to Data guard let jsonData = jsonString.data(using: .utf8) else { fatalError("Failed to convert JSON string to data") } do { // Decode the full response let fullResponse = try JSONDecoder().decode(ReportResponse.self, from: jsonData) // Extract the report node let report = fullResponse.report // Access specific data points from the report print("Total purchases: \(report.purchase.total)") print("Average sale amount: \(report.sale.average)") print("Minimum production value: \(report.production.min)") } catch { print("Decoding error: \(error.localizedDescription)") }
Bonus: Directly Decode the report Node (If You Don't Need earliest/latest)
If you don't care about the top-level earliest and latest fields, you can extract the report node first before decoding:
do { // Convert JSON data to a dictionary let jsonDict = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any] // Extract the report dictionary guard let reportDict = jsonDict?["report"] as? [String: Any] else { fatalError("Couldn't find the report node in JSON") } // Convert the report dictionary back to Data let reportData = try JSONSerialization.data(withJSONObject: reportDict) // Decode directly to the Report struct let report = try JSONDecoder().decode(Report.self, from: reportData) print(report) } catch { print("Error: \(error.localizedDescription)") }
This approach is less type-safe than decoding the full response, but it works if you only need the report data.
内容的提问来源于stack exchange,提问作者Jatin Menghani

