JSON 转 Swift Codable 工具
从 JSON 生成遵循 Codable 的 Swift 结构体,并附带便于解码的便捷初始化方法。
从 JSON 生成 Swift Codable 结构体
Codable 是 Swift 中解码 JSON 的标准方式,但为庞大的数据结构手写模型非常繁琐。本工具会为每个对象生成一个遵循 Codable 的 struct,使用 let 属性,类型包括 Int、Double、String、Bool、嵌套结构体和数组。当 JSON 键名与 Swift 属性名不同时,会添加 CodingKeys 枚举;同时生成的扩展还提供可从 Data、JSON 字符串或 URL 解码的初始化方法、用于创建修改副本的 with(...) 方法,以及用于重新编码的 jsonData()/jsonString()。
使用方法
- 粘贴一段 JSON 样例,例如你的 iOS 或 macOS 应用收到的响应。
- 输入根类型名称,例如 Article。
- 将文件添加到 Xcode 项目中,使用 try Article(jsonString) 或 JSONDecoder().decode(Article.self, from: data) 进行解码。
常见问题
- 可以配合 SwiftUI 和 URLSession 使用吗?
- 可以。这些结构体是普通的 Codable 值类型,你可以解码 URLSession 返回的 Data,并像使用其他模型一样把结果传给 SwiftUI 视图。
- snake_case 键名如何处理?
- 属性会使用 Swift 风格的 camelCase 命名(is_featured 变为 isFeatured),并由 CodingKeys 枚举将其映射回原始 JSON 键,因此无需设置 keyDecodingStrategy。
- 可以删除那些便捷扩展吗?
- 可以。Codable 只需要结构体声明和 CodingKeys。扩展以及 newJSONDecoder/newJSONEncoder 辅助函数都是可选的便捷功能,可以删除。
示例:JSON 转 Swift
输入 JSON
{
"id": 3,
"title": "Hello Swift",
"author": { "name": "Taylor", "followers": 1200 },
"is_featured": true
}生成的 Swift 代码(Article)
// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse the JSON, add this file to your project and do:
//
// let article = try Article(json)
import Foundation
// MARK: - Article
struct Article: Codable {
let id: Int
let title: String
let author: Author
let isFeatured: Bool
enum CodingKeys: String, CodingKey {
case id, title, author
case isFeatured = "is_featured"
}
}
// MARK: Article convenience initializers and mutators
extension Article {
init(data: Data) throws {
self = try newJSONDecoder().decode(Article.self, from: data)
}
init(_ json: String, using encoding: String.Encoding = .utf8) throws {
guard let data = json.data(using: encoding) else {
throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
}
try self.init(data: data)
}
init(fromURL url: URL) throws {
try self.init(data: try Data(contentsOf: url))
}
func with(
id: Int? = nil,
title: String? = nil,
author: Author? = nil,
isFeatured: Bool? = nil
) -> Article {
return Article(
id: id ?? self.id,
title: title ?? self.title,
author: author ?? self.author,
isFeatured: isFeatured ?? self.isFeatured
)
}
func jsonData() throws -> Data {
return try newJSONEncoder().encode(self)
}
func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
return String(data: try self.jsonData(), encoding: encoding)
}
}
// MARK: - Author
struct Author: Codable {
let name: String
let followers: Int
}
// MARK: Author convenience initializers and mutators
extension Author {
init(data: Data) throws {
self = try newJSONDecoder().decode(Author.self, from: data)
}
init(_ json: String, using encoding: String.Encoding = .utf8) throws {
guard let data = json.data(using: encoding) else {
throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
}
try self.init(data: data)
}
init(fromURL url: URL) throws {
try self.init(data: try Data(contentsOf: url))
}
func with(
name: String? = nil,
followers: Int? = nil
) -> Author {
return Author(
name: name ?? self.name,
followers: followers ?? self.followers
)
}
func jsonData() throws -> Data {
return try newJSONEncoder().encode(self)
}
func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
return String(data: try self.jsonData(), encoding: encoding)
}
}
// MARK: - Helper functions for creating encoders and decoders
func newJSONDecoder() -> JSONDecoder {
let decoder = JSONDecoder()
if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
decoder.dateDecodingStrategy = .iso8601
}
return decoder
}
func newJSONEncoder() -> JSONEncoder {
let encoder = JSONEncoder()
if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
encoder.dateEncodingStrategy = .iso8601
}
return encoder
}