# SinoHandyCodable **Repository Path**: mashouyin/sino-handy-codable ## Basic Information - **Project Name**: SinoHandyCodable - **Description**: 一个基于 Swift Codable 的强大 JSON 解析库,提供类似 HandyJSON 的便捷使用体验,同时保持 Codable 的类型安全和性能优势。 - **Primary Language**: Swift - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-01-28 - **Last Updated**: 2026-01-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # HandyCodable - Swift JSON 解析库 一个结合了 HandyJSON 易用性和 Codable 安全性的 Swift JSON 解析库。 ## 特性 ✅ **简单易用** - 类似 HandyJSON 的 API 设计 ✅ **类型安全** - 基于 Swift Codable 实现 ✅ **容错性强** - 字段缺失、类型不匹配不会导致解析失败 ✅ **智能转换** - 支持 String ↔ Int、String ↔ Bool 等常见类型转换 ✅ **零依赖** - 仅依赖 Foundation 框架 ✅ **高性能** - Release 版本无日志开销 ## 快速开始 ### 1. 定义模型 ```swift import Foundation struct User: SinoHandyCodable { @SinoHandySafe var id: Int? @SinoHandyDefault var name: String @SinoHandySafe var email: String? @SinoHandyDefault(wrappedValue: 1) var age: Int @SinoHandyDefault(wrappedValue: []) var tags: [String] init() {} } ``` ### 2. 解析 JSON ```swift // 从 JSON 字符串解析 let jsonString = """ { "id": 123, "name": "Alice", "email": "alice@example.com", "age": 25, "tags": ["swift", "ios"] } """ if let user = User.deserialize(from: jsonString) { print("用户名: \(user.name)") print("年龄: \(user.age)") } // 从字典解析 let dict: [String: Any] = [ "id": 456, "name": "Bob" ] if let user = User.deserialize(from: dict) { print("用户名: \(user.name)") } // 从 Data 解析 if let data = jsonString.data(using: .utf8), let user = User.deserialize(from: data) { print("用户名: \(user.name)") } ``` ### 3. 解析数组 ```swift let jsonArray = """ [ {"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}, {"id": 3, "name": "Charlie"} ] """ if let users = [User].deserialize(from: jsonArray) { print("用户数量: \(users.count)") users.forEach { print($0.name) } } ``` ### 4. 编码为 JSON ```swift var user = User() user.name = "Alice" user.age = 25 user.tags = ["swift", "ios"] // 转为 JSON 字符串 if let jsonString = user.toJSONString() { print(jsonString) } // 转为格式化的 JSON 字符串 if let prettyJSON = user.toJSONString(pretty: true) { print(prettyJSON) } // 转为字典 if let dict = user.toDictionary() { print(dict) } // 转为 Data if let data = user.toJSONData() { print("Data size: \(data.count) bytes") } ``` ## 属性包装器 ### @SinoHandySafe - 容错包装器 用于可选字段,字段缺失或类型不匹配时返回 `nil`,不会导致整个解析失败。 ```swift struct Model: SinoHandyCodable { @SinoHandySafe var optionalField: String? @SinoHandySafe var optionalNumber: Int? init() {} } ``` **特性:** - 字段缺失 → `nil` - 类型不匹配 → `nil` - 支持智能类型转换 ### @SinoHandyDefault - 默认值包装器 用于必填字段,字段缺失或解析失败时使用默认值。 ```swift struct Model: SinoHandyCodable { @SinoHandyDefault(wrappedValue: "") var name: String // 默认 "" @SinoHandyDefault(wrappedValue: 0) var count: Int // 默认 0 @SinoHandyDefault(wrappedValue: 0.0) var price: Double // 默认 0.0 @SinoHandyDefault(wrappedValue: false) var isActive: Bool // 默认 false @SinoHandyDefault(wrappedValue: []) var items: [String] // 默认 [] @SinoHandyDefault(wrappedValue: [:]) var info: [String: Any] // 默认 [:] init() {} } ``` **支持的默认值类型:** - `String` → `""` - `Int/Int8/Int16/Int32/Int64` → `0` - `UInt/UInt8/UInt16/UInt32/UInt64` → `0` - `Double/Float/CGFloat` → `0.0` - `Bool` → `false` - `Array` → `[]` - `Dictionary` → `[:]` ## 智能类型转换 `@SinoHandySafe` 支持以下类型转换: ### String → 数字类型 ```swift // JSON: {"count": "123"} @SinoHandySafe var count: Int? // 结果: 123 // JSON: {"price": "99.99"} @SinoHandySafe var price: Double? // 结果: 99.99 ``` ### String → Bool ```swift // JSON: {"flag": "true"} 或 "1" 或 "yes" @SinoHandySafe var flag: Bool? // 结果: true // JSON: {"flag": "false"} 或 "0" 或 "no" @SinoHandySafe var flag: Bool? // 结果: false ``` ### 数字 → String ```swift // JSON: {"id": 123} @SinoHandySafe var id: String? // 结果: "123" // JSON: {"price": 99.99} @SinoHandySafe var price: String? // 结果: "99.99" ``` ### Int → Bool ```swift // JSON: {"flag": 1} @SinoHandySafe var flag: Bool? // 结果: true // JSON: {"flag": 0} @SinoHandySafe var flag: Bool? // 结果: false ``` ### Double ↔ Int ```swift // JSON: {"value": 123.45} @SinoHandySafe var value: Int? // 结果: 123 (向下取整) // JSON: {"value": 100} @SinoHandySafe var value: Double? // 结果: 100.0 ``` ## 容错示例 ### 字段缺失 ```swift struct User: SinoHandyCodable { @SinoHandySafe var id: Int? @SinoHandyDefault(wrappedValue: "") var name: String init() {} } // JSON 缺少 id 字段 let json = """ {"name": "Alice"} """ if let user = User.deserialize(from: json) { print(user.id) // nil print(user.name) // "Alice" } ``` ### 类型不匹配 ```swift // JSON 中 id 是字符串,但模型定义为 Int let json = """ {"id": "123", "name": "Alice"} """ if let user = User.deserialize(from: json) { print(user.id) // 123 (自动转换) print(user.name) // "Alice" } ``` ### null 值处理 ```swift let json = """ {"id": null, "name": "Alice"} """ if let user = User.deserialize(from: json) { print(user.id) // nil print(user.name) // "Alice" } ``` ## 自定义默认值 如果需要为自定义类型提供默认值,只需实现 `DefaultValue` 协议: ```swift struct CustomType: Codable, DefaultValue { var value: String static var defaultValue: CustomType { CustomType(value: "default") } } struct Model: SinoHandyCodable { @SinoHandyDefault var custom: CustomType init() {} } ``` ## 性能优化 ### Debug vs Release 所有日志输出都使用 `#if DEBUG` 条件编译: - **Debug 模式**:输出详细的解析日志,便于调试 - **Release 模式**:完全移除日志代码,零性能开销 ### 最佳实践 1. **优先使用 @SinoHandySafe**:对于可选字段,使用 `@SinoHandySafe` 提供最大的容错性 2. **合理使用 @SinoHandyDefault**:对于必填字段且有合理默认值的情况使用 3. **避免过度嵌套**:深层嵌套会影响解析性能 4. **复用模型**:相同结构的 JSON 使用相同的模型定义 ## 错误处理 解析失败时返回 `nil`,不会抛出异常: ```swift let invalidJSON = "invalid json" if let user = User.deserialize(from: invalidJSON) { print("解析成功") } else { print("解析失败,请检查 JSON 格式") } ``` ## 测试 运行测试用例: ```swift // 在 ViewController 或其他地方调用 runAllTests() ``` 测试覆盖: - ✅ 基本类型解析 - ✅ 类型转换 - ✅ 字段缺失处理 - ✅ null 值处理 - ✅ 数组解析 - ✅ 编码功能 ## 文件结构 ``` HandyCodable/ ├── SinoHandyCodable.swift # 核心协议和反序列化 ├── SinoHandySafe.swift # @Safe 容错包装器 ├── SinoHandyDefault.swift # @Default 默认值包装器 ├── SinoHandyEncodable+Extension.swift # 编码扩展 ├── HandyCodableTests.swift # 测试用例 ├── README.md # 使用文档 └── OPTIMIZATION_NOTES.md # 优化说明 ``` ## 系统要求 - iOS 13.0+ - macOS 10.15+ - Swift 5.0+ ## License MIT License ## 更新日志 ### v2.0 (2026-01-28) - ✅ 重构 `@SinoHandyDefault`,引入 `DefaultValue` 协议 - ✅ 移除所有不安全的强制类型转换 - ✅ 添加 `Equatable` 和 `Hashable` 支持 - ✅ 优化智能类型转换逻辑 - ✅ 添加 `#if DEBUG` 条件编译 - ✅ 新增 `toJSONData()` 方法 - ✅ 改进错误处理和日志输出 - ✅ 完善文档和测试用例 ======= # SinoHandyCodable 一个基于 Swift Codable 的强大 JSON 解析库,提供类似 HandyJSON 的便捷使用体验,同时保持 Codable 的类型安全和性能优势。 ## ✨ 特性 - 🚀 **简洁的 API**:一行代码完成 JSON 解析 - 🛡️ **类型安全**:基于 Swift Codable,编译时类型检查 - 🔄 **智能类型转换**:自动处理 String ↔ Int、String ↔ Bool 等常见转换 - 💪 **容错能力强**:字段缺失或类型不匹配不会导致整体解析失败 - 📦 **零依赖**:纯 Swift 实现,无需第三方库 - 🎯 **灵活的默认值**:支持自定义默认值策略 ## 📋 目录 - [安装](#安装) - [快速开始](#快速开始) - [核心功能](#核心功能) - [基础解析](#基础解析) - [容错解析 @Safe](#容错解析-safe) - [默认值 @Default](#默认值-default) - [模型序列化](#模型序列化) - [高级用法](#高级用法) - [完整示例](#完整示例) - [API 文档](#api-文档) ## 🔧 安装 将 `HandyCodable` 文件夹复制到你的项目中: ``` HandyCodable/ ├── SinoHandyCodable.swift ├── SinoHandySafe.swift ├── SinoHandyDefault.swift └── SinoHandyEncodable+Extension.swift ``` ## 🚀 快速开始 ### 1. 定义模型 ```swift import UIKit struct User: SinoHandyCodable { var id: Int var name: String var email: String init() { id = 0 name = "" email = "" } } ``` ### 2. 解析 JSON ```swift let jsonString = """ { "id": 1001, "name": "张三", "email": "zhangsan@example.com" } """ if let user = User.deserialize(from: jsonString) { print("用户 ID: \(user.id)") print("用户名: \(user.name)") print("邮箱: \(user.email)") } ``` ### 3. 序列化为 JSON ```swift let user = User() user.id = 1001 user.name = "张三" user.email = "zhangsan@example.com" // 转为 JSON 字符串 if let jsonString = user.toJSONString(pretty: true) { print(jsonString) } // 转为字典 if let dict = user.toDictionary() { print(dict) } ``` ## 🎯 核心功能 ### 基础解析 `SinoHandyCodable` 协议提供了统一的解析接口,支持多种输入格式: #### 从 JSON 字符串解析 ```swift struct Product: SinoHandyCodable { var id: Int var name: String var price: Double init() { id = 0 name = "" price = 0.0 } } let jsonString = """ { "id": 2001, "name": "iPhone 15 Pro", "price": 7999.00 } """ if let product = Product.deserialize(from: jsonString) { print("商品: \(product.name), 价格: ¥\(product.price)") } ``` #### 从字典解析 ```swift let dict: [String: Any] = [ "id": 2001, "name": "iPhone 15 Pro", "price": 7999.00 ] if let product = Product.deserialize(from: dict) { print("商品: \(product.name)") } ``` #### 从 Data 解析 ```swift if let data = jsonString.data(using: .utf8), let product = Product.deserialize(from: data) { print("商品: \(product.name)") } ``` #### 解析数组 ```swift let jsonArray = """ [ {"id": 1, "name": "商品A", "price": 99.00}, {"id": 2, "name": "商品B", "price": 199.00}, {"id": 3, "name": "商品C", "price": 299.00} ] """ if let products = [Product].deserialize(from: jsonArray) { print("共 \(products.count) 个商品") products.forEach { product in print("- \(product.name): ¥\(product.price)") } } ``` ### 容错解析 @Safe `@SinoHandySafe` 属性包装器提供强大的容错能力,让你的解析更加健壮。 #### 特性 - ✅ 字段缺失时返回 `nil`,不影响其他字段解析 - ✅ 类型不匹配时返回 `nil`,不会导致整体解析失败 - ✅ 智能类型转换:String ↔ Int、String ↔ Double、String ↔ Bool - ✅ 支持 null 值 #### 基础用法 ```swift struct UserProfile: SinoHandyCodable { @SinoHandySafe var userId: Int? @SinoHandySafe var username: String? @SinoHandySafe var avatar: String? @SinoHandySafe var isVip: Bool? @SinoHandySafe var score: Double? init() {} } // 测试 1: 完整数据 let json1 = """ { "userId": 1001, "username": "张三", "avatar": "https://example.com/avatar.jpg", "isVip": true, "score": 98.5 } """ if let profile = UserProfile.deserialize(from: json1) { print("用户 ID: \(profile.userId ?? 0)") print("用户名: \(profile.username ?? "未知")") print("VIP: \(profile.isVip ?? false)") print("积分: \(profile.score ?? 0)") } // 测试 2: 部分字段缺失 let json2 = """ { "userId": 1002, "username": "李四" } """ if let profile = UserProfile.deserialize(from: json2) { print("用户 ID: \(profile.userId ?? 0)") print("用户名: \(profile.username ?? "未知")") print("头像: \(profile.avatar ?? "无")") // 缺失字段,返回 nil print("VIP: \(profile.isVip ?? false)") // 缺失字段,返回 nil } // 测试 3: 所有字段缺失 let json3 = "{}" if let profile = UserProfile.deserialize(from: json3) { print("解析成功,所有字段为 nil") print("用户 ID: \(profile.userId?.description ?? "nil")") } ``` #### 智能类型转换 `@SinoHandySafe` 支持常见的类型自动转换: ```swift struct ConversionTest: SinoHandyCodable { @SinoHandySafe var intValue: Int? @SinoHandySafe var doubleValue: Double? @SinoHandySafe var stringValue: String? @SinoHandySafe var boolValue: Bool? init() {} } // String → 数字类型 let json = """ { "intValue": "123", "doubleValue": "45.67", "stringValue": 999, "boolValue": "true" } """ if let model = ConversionTest.deserialize(from: json) { print("intValue: \(model.intValue ?? 0)") // 123 (String "123" → Int) print("doubleValue: \(model.doubleValue ?? 0)") // 45.67 (String "45.67" → Double) print("stringValue: \(model.stringValue ?? "")") // "999" (Int 999 → String) print("boolValue: \(model.boolValue ?? false)") // true (String "true" → Bool) } ``` #### 支持的类型转换 | 源类型 | 目标类型 | 示例 | |--------|----------|------| | String | Int | `"123"` → `123` | | String | Double | `"45.67"` → `45.67` | | String | Float | `"3.14"` → `3.14` | | String | Bool | `"true"` → `true`, `"1"` → `true` | | Int | String | `123` → `"123"` | | Double | String | `45.67` → `"45.67"` | | Bool | String | `true` → `"true"` | | Int | Bool | `1` → `true`, `0` → `false` | #### 实际应用场景 ```swift // 场景:后端返回的数据类型不稳定 struct PackageInfo: SinoHandyCodable { @SinoHandySafe var flag: Int? // 可能是 Int 或 String @SinoHandySafe var sign: String? // 可能缺失 @SinoHandySafe var packageName: String? // 可能缺失 init() {} } // 情况 1: flag 是字符串 let json1 = """ {"flag":"16","sign":"abc","packageName":"com.example.app"} """ // 情况 2: flag 是数字 let json2 = """ {"flag":16,"sign":"abc","packageName":"com.example.app"} """ // 情况 3: 部分字段缺失 let json3 = """ {"flag":"16"} """ // 以上三种情况都能正常解析 [json1, json2, json3].forEach { json in if let package = PackageInfo.deserialize(from: json) { print("flag: \(package.flag ?? 0)") print("sign: \(package.sign ?? "无")") print("packageName: \(package.packageName ?? "无")") print("---") } } ``` ### 默认值 @Default `@SinoHandyDefault` 属性包装器允许你为字段指定默认值。 #### 基础用法 ```swift struct AppConfig: SinoHandyCodable { @SinoHandyDefault(wrappedValue: "默认应用名") var appName: String @SinoHandyDefault(wrappedValue: 1) var version: Int @SinoHandyDefault(wrappedValue: true) var isEnabled: Bool @SinoHandyDefault(wrappedValue: 0.0) var timeout: Double init() {} } let json = """ { "appName": "我的应用" } """ if let config = AppConfig.deserialize(from: json) { print("应用名: \(config.appName)") // "我的应用" print("版本: \(config.version)") // 1 (使用默认值) print("启用: \(config.isEnabled)") // true (使用默认值) print("超时: \(config.timeout)") // 0.0 (使用默认值) } ``` #### 支持的默认值类型 - String → `""` - Int, Int8, Int16, Int32, Int64 → `0` - UInt, UInt8, UInt16, UInt32, UInt64 → `0` - Double, Float, CGFloat → `0.0` - Bool → `false` ### 模型序列化 将模型转换为 JSON 字符串或字典。 #### 转为 JSON 字符串 ```swift struct Article: SinoHandyCodable { var id: Int var title: String var content: String var author: String init() { id = 0 title = "" content = "" author = "" } } var article = Article() article.id = 1001 article.title = "Swift 编程指南" article.content = "这是一篇关于 Swift 的文章..." article.author = "张三" // 紧凑格式 if let jsonString = article.toJSONString() { print(jsonString) // {"id":1001,"title":"Swift 编程指南","content":"这是一篇关于 Swift 的文章...","author":"张三"} } // 格式化输出 if let prettyJSON = article.toJSONString(pretty: true) { print(prettyJSON) /* { "id" : 1001, "title" : "Swift 编程指南", "content" : "这是一篇关于 Swift 的文章...", "author" : "张三" } */ } ``` #### 转为字典 ```swift if let dict = article.toDictionary() { print(dict) // ["id": 1001, "title": "Swift 编程指南", "content": "这是一篇关于 Swift 的文章...", "author": "张三"] // 可以用于网络请求参数 let parameters = dict // 发送网络请求... } ``` ## 🔥 高级用法 ### 嵌套模型 ```swift struct Address: SinoHandyCodable { @SinoHandySafe var province: String? @SinoHandySafe var city: String? @SinoHandySafe var street: String? init() {} } struct Company: SinoHandyCodable { @SinoHandySafe var name: String? @SinoHandySafe var address: Address? init() {} } struct Employee: SinoHandyCodable { @SinoHandySafe var id: Int? @SinoHandySafe var name: String? @SinoHandySafe var company: Company? init() {} } let json = """ { "id": 1001, "name": "王五", "company": { "name": "科技公司", "address": { "province": "广东省", "city": "深圳市", "street": "南山区科技园" } } } """ if let employee = Employee.deserialize(from: json) { print("员工: \(employee.name ?? "未知")") print("公司: \(employee.company?.name ?? "未知")") print("地址: \(employee.company?.address?.city ?? "未知")") } ``` ### 数组嵌套 ```swift struct Comment: SinoHandyCodable { @SinoHandySafe var id: Int? @SinoHandySafe var content: String? @SinoHandySafe var author: String? init() {} } struct Post: SinoHandyCodable { @SinoHandySafe var id: Int? @SinoHandySafe var title: String? @SinoHandySafe var comments: [Comment]? init() {} } let json = """ { "id": 1, "title": "精彩文章", "comments": [ {"id": 101, "content": "写得不错", "author": "用户A"}, {"id": 102, "content": "学到了", "author": "用户B"} ] } """ if let post = Post.deserialize(from: json) { print("文章: \(post.title ?? "未知")") print("评论数: \(post.comments?.count ?? 0)") post.comments?.forEach { comment in print("- \(comment.author ?? "匿名"): \(comment.content ?? "")") } } ``` ### 混合使用 @Safe 和 @Default ```swift struct MixedModel: SinoHandyCodable { // 必填字段,使用默认值 @SinoHandyDefault(wrappedValue: 0) var id: Int @SinoHandyDefault(wrappedValue: "未命名") var name: String // 可选字段,使用 Safe @SinoHandySafe var email: String? @SinoHandySafe var phone: String? @SinoHandySafe var age: Int? init() {} } let json = """ { "id": 1001, "email": "test@example.com" } """ if let model = MixedModel.deserialize(from: json) { print("ID: \(model.id)") // 1001 print("姓名: \(model.name)") // "未命名" (使用默认值) print("邮箱: \(model.email ?? "无")") // "test@example.com" print("电话: \(model.phone ?? "无")") // "无" (字段缺失) } ``` ### 自定义 CodingKeys ```swift struct CustomKeysModel: SinoHandyCodable { @SinoHandySafe var userId: Int? @SinoHandySafe var userName: String? @SinoHandySafe var userEmail: String? enum CodingKeys: String, CodingKey { case userId = "user_id" case userName = "user_name" case userEmail = "user_email" } init() {} } // JSON 使用下划线命名 let json = """ { "user_id": 1001, "user_name": "张三", "user_email": "zhangsan@example.com" } """ if let model = CustomKeysModel.deserialize(from: json) { print("用户 ID: \(model.userId ?? 0)") print("用户名: \(model.userName ?? "未知")") } ``` ## 📚 完整示例 ### 电商应用示例 ```swift // 商品模型 struct Product: SinoHandyCodable { @SinoHandySafe var id: Int? @SinoHandySafe var name: String? @SinoHandySafe var price: Double? @SinoHandySafe var originalPrice: Double? @SinoHandySafe var stock: Int? @SinoHandySafe var imageUrl: String? @SinoHandySafe var description: String? @SinoHandySafe var tags: [String]? init() {} } // 订单模型 struct Order: SinoHandyCodable { @SinoHandySafe var orderId: String? @SinoHandySafe var userId: Int? @SinoHandySafe var products: [Product]? @SinoHandySafe var totalAmount: Double? @SinoHandySafe var status: String? @SinoHandySafe var createTime: String? init() {} } // 用户模型 struct User: SinoHandyCodable { @SinoHandySafe var id: Int? @SinoHandySafe var username: String? @SinoHandySafe var avatar: String? @SinoHandySafe var level: Int? @SinoHandySafe var orders: [Order]? init() {} } // 模拟 API 响应 let apiResponse = """ { "id": 1001, "username": "购物达人", "avatar": "https://example.com/avatar.jpg", "level": 5, "orders": [ { "orderId": "ORD20260128001", "userId": 1001, "totalAmount": 299.00, "status": "已发货", "createTime": "2026-01-28 10:30:00", "products": [ { "id": 2001, "name": "无线蓝牙耳机", "price": 299.00, "originalPrice": 399.00, "stock": 100, "imageUrl": "https://example.com/product1.jpg", "tags": ["数码", "热销"] } ] } ] } """ if let user = User.deserialize(from: apiResponse) { print("用户: \(user.username ?? "未知")") print("等级: Lv.\(user.level ?? 0)") print("订单数: \(user.orders?.count ?? 0)") user.orders?.forEach { order in print("\n订单号: \(order.orderId ?? "未知")") print("状态: \(order.status ?? "未知")") print("金额: ¥\(order.totalAmount ?? 0)") order.products?.forEach { product in print(" - \(product.name ?? "未知"): ¥\(product.price ?? 0)") } } } ``` ### 网络请求示例 ```swift // API 响应模型 struct APIResponse: SinoHandyCodable { @SinoHandySafe var code: Int? @SinoHandySafe var message: String? @SinoHandySafe var data: T? init() {} } // 使用示例 func fetchUserInfo(userId: Int, completion: @escaping (User?) -> Void) { // 模拟网络请求 let responseJSON = """ { "code": 200, "message": "success", "data": { "id": 1001, "username": "张三", "avatar": "https://example.com/avatar.jpg" } } """ if let response = APIResponse.deserialize(from: responseJSON) { if response.code == 200 { completion(response.data) } else { print("错误: \(response.message ?? "未知错误")") completion(nil) } } } // 调用 fetchUserInfo(userId: 1001) { user in if let user = user { print("获取用户信息成功: \(user.username ?? "未知")") } } ``` ## 📖 API 文档 ### SinoHandyCodable 协议 ```swift public protocol SinoHandyCodable: Codable { init() } ``` #### 方法 ##### deserialize(from:) 从 JSON 数据解析为模型对象。 ```swift static func deserialize(from json: Any?) -> Self? ``` **参数:** - `json`: 可以是 `String`、`Data` 或 `[String: Any]` **返回值:** - 解析成功返回模型实例,失败返回 `nil` **示例:** ```swift let user = User.deserialize(from: jsonString) let user = User.deserialize(from: jsonData) let user = User.deserialize(from: dictionary) ``` ### Array Extension ```swift extension Array where Element: SinoHandyCodable ``` #### 方法 ##### deserialize(from:) 从 JSON 数组解析为模型数组。 ```swift static func deserialize(from json: Any?) -> [Element]? ``` **参数:** - `json`: 可以是 `String`、`Data` 或 `[[String: Any]]` **返回值:** - 解析成功返回模型数组,失败返回 `nil` **示例:** ```swift let users = [User].deserialize(from: jsonArrayString) ``` ### Encodable Extension ```swift extension Encodable ``` #### 方法 ##### toJSONString(pretty:) 将模型转换为 JSON 字符串。 ```swift func toJSONString(pretty: Bool = false) -> String? ``` **参数:** - `pretty`: 是否格式化输出,默认 `false` **返回值:** - JSON 字符串,失败返回 `nil` **示例:** ```swift let jsonString = user.toJSONString() let prettyJSON = user.toJSONString(pretty: true) ``` ##### toDictionary() 将模型转换为字典。 ```swift func toDictionary() -> [String: Any]? ``` **返回值:** - 字典,失败返回 `nil` **示例:** ```swift let dict = user.toDictionary() ``` ### @SinoHandySafe 容错属性包装器,字段缺失或类型不匹配时返回 `nil`。 ```swift @propertyWrapper public struct SinoHandySafe: Codable ``` **使用:** ```swift @SinoHandySafe var name: String? @SinoHandySafe var age: Int? ``` **特性:** - 字段缺失 → `nil` - 类型不匹配 → `nil` - 支持智能类型转换 - 不影响其他字段解析 ### @SinoHandyDefault 默认值属性包装器,字段缺失时使用指定的默认值。 ```swift @propertyWrapper public struct SinoHandyDefault: Codable, Equatable ``` **使用:** ```swift @SinoHandyDefault(wrappedValue: "默认值") var name: String @SinoHandyDefault(wrappedValue: 0) var count: Int ``` **支持的类型:** - String, Int, Double, Float, CGFloat, Bool - 以及它们的变体(Int8, Int16, UInt 等) ## ⚠️ 注意事项 1. **必须实现 init()** ```swift struct Model: SinoHandyCodable { init() {} // 必须 } ``` 2. **@Safe 用于可选字段** ```swift @SinoHandySafe var name: String? // ✅ 正确 @SinoHandySafe var name: String // ❌ 错误 ``` 3. **@Default 用于非可选字段** ```swift @SinoHandyDefault(wrappedValue: "") var name: String // ✅ 正确 @SinoHandyDefault(wrappedValue: "") var name: String? // ❌ 不推荐 ``` 4. **类型转换限制** - 只支持常见的基础类型转换 - 复杂对象不支持自动转换 ## 🆚 对比其他方案 ### vs HandyJSON | 特性 | SinoHandyCodable | HandyJSON | |------|------------------|-----------| | 类型安全 | ✅ 编译时检查 | ❌ 运行时反射 | | 性能 | ✅ 高 | ⚠️ 较低 | | Swift 版本 | ✅ 支持最新版本 | ⚠️ 可能不兼容 | | 使用便捷性 | ✅ 简洁 | ✅ 简洁 | | 容错能力 | ✅ 强 | ✅ 强 | ### vs 原生 Codable | 特性 | SinoHandyCodable | 原生 Codable | |------|------------------|--------------| | 使用便捷性 | ✅ 一行代码 | ❌ 需要手动处理 | | 容错能力 | ✅ 强 | ❌ 弱 | | 类型转换 | ✅ 自动 | ❌ 需手动实现 | | 性能 | ✅ 相同 | ✅ 高 | ## 📝 更新日志 ### v1.0.0 (2026-01-28) - ✨ 初始版本发布 - ✅ 支持基础 JSON 解析 - ✅ 实现 @Safe 容错机制 - ✅ 实现 @Default 默认值 - ✅ 支持智能类型转换 - ✅ 支持模型序列化 ## 📄 许可证 MIT License ## 🤝 贡献 欢迎提交 Issue 和 Pull Request! ## 📮 联系方式 如有问题或建议,请通过以下方式联系: - 提交 Issue - 发送邮件 --- **享受便捷的 JSON 解析体验!** 🎉