如何解析 Swift 中的本地 JSON 数据?
How to parse local JSON data in Swift?
如何解析本地JSON数据where nested (optional) 属性 is same as main.
项目数据可能可用也可能不可用。
struct Category: Identifiable, Codable {
let id: Int
let name: String
let image: String
var items: [Category]?
}
我正在使用通用的 Bundle 扩展来解析 JSON 数据。
extension Bundle {
func decode<T: Codable>(_ file: String) -> T {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) from bundle.")
}
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "y-MM-dd"
decoder.dateDecodingStrategy = .formatted(formatter)
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError("Failed to decode \(file) from bundle.")
}
return loaded
}
}
例如数据:
[
{
"id": 1,
"name": "Apple",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "iPhone",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "iPhone 11 Pro",
"image": "img_url"
},
{
"id": 2,
"name": "iPhone 11 Pro Max",
"image": "img_url"
}
]
},
{
"id": 2,
"name": "iPad",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "iPad mini",
"image": "img_url"
},
{
"id": 2,
"name": "iPad Air",
"image": "img_url"
},
{
"id": 3,
"name": "iPad Pro",
"image": "img_url"
}
]
}
]
},
{
"id": 2,
"name": "Samsung",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "Phone",
"image": "img_url"
},
{
"id": 2,
"name": "Tablet",
"image": "img_url"
}
]
}
]
嵌套不是这里的问题,您面对的是 Array
个 Content
。所以你应该将 [Content]
传递给 decoder
,例如:
let jsonDecoder = JSONDecoder()
try! jsonDecoder.decode([Category].self, from: json)
属性 包装器
您可以实现一个简单的 属性 包装器来加载和解码您的所有属性:
@propertyWrapper struct BundleFile<DataType: Decodable> {
let name: String
let type: String = "json"
let fileManager: FileManager = .default
let bundle: Bundle = .main
let decoder = JSONDecoder()
var wrappedValue: DataType {
guard let path = bundle.path(forResource: name, ofType: type) else { fatalError("Resource not found") }
guard let data = fileManager.contents(atPath: path) else { fatalError("File not loaded") }
return try! decoder.decode(DataType.self, from: data)
}
}
现在您可以从 Bundle
中的文件加载任何 属性,例如:
@BundleFile(name: "MyFile")
var contents: [Content]
注意,由于属性应该从bundle中加载,所以我提出了一个FatalError
。因为唯一应该对这些错误负责的人是代码时间的开发人员(而不是 运行 时间)。
如何解析本地JSON数据where nested (optional) 属性 is same as main.
项目数据可能可用也可能不可用。
struct Category: Identifiable, Codable {
let id: Int
let name: String
let image: String
var items: [Category]?
}
我正在使用通用的 Bundle 扩展来解析 JSON 数据。
extension Bundle {
func decode<T: Codable>(_ file: String) -> T {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) from bundle.")
}
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "y-MM-dd"
decoder.dateDecodingStrategy = .formatted(formatter)
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError("Failed to decode \(file) from bundle.")
}
return loaded
}
}
例如数据:
[
{
"id": 1,
"name": "Apple",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "iPhone",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "iPhone 11 Pro",
"image": "img_url"
},
{
"id": 2,
"name": "iPhone 11 Pro Max",
"image": "img_url"
}
]
},
{
"id": 2,
"name": "iPad",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "iPad mini",
"image": "img_url"
},
{
"id": 2,
"name": "iPad Air",
"image": "img_url"
},
{
"id": 3,
"name": "iPad Pro",
"image": "img_url"
}
]
}
]
},
{
"id": 2,
"name": "Samsung",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "Phone",
"image": "img_url"
},
{
"id": 2,
"name": "Tablet",
"image": "img_url"
}
]
}
]
嵌套不是这里的问题,您面对的是 Array
个 Content
。所以你应该将 [Content]
传递给 decoder
,例如:
let jsonDecoder = JSONDecoder()
try! jsonDecoder.decode([Category].self, from: json)
属性 包装器
您可以实现一个简单的 属性 包装器来加载和解码您的所有属性:
@propertyWrapper struct BundleFile<DataType: Decodable> {
let name: String
let type: String = "json"
let fileManager: FileManager = .default
let bundle: Bundle = .main
let decoder = JSONDecoder()
var wrappedValue: DataType {
guard let path = bundle.path(forResource: name, ofType: type) else { fatalError("Resource not found") }
guard let data = fileManager.contents(atPath: path) else { fatalError("File not loaded") }
return try! decoder.decode(DataType.self, from: data)
}
}
现在您可以从 Bundle
中的文件加载任何 属性,例如:
@BundleFile(name: "MyFile")
var contents: [Content]
注意,由于属性应该从bundle中加载,所以我提出了一个FatalError
。因为唯一应该对这些错误负责的人是代码时间的开发人员(而不是 运行 时间)。