You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift属性包装器适配基础类型JSON解码问题求助

解决Swift属性包装器同时解码基础类型的两种JSON格式问题

问题场景

需要通过UUIDEmbedded属性包装器同时兼容两种JSON解码场景:

  • 场景1:JSON是包含id和value字段的字典结构(如{"id": "uuid-string", "value": 25})
  • 场景2:JSON是直接的目标类型值(如25)

当前实现的属性包装器在自定义结构体(如Person)上工作正常,但用于基础类型(如Person中的age: Int)时,仅能解码场景1的JSON,解码场景2会抛出类型不匹配错误:

typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "info", intValue: nil), CodingKeys(stringValue: "value", intValue: nil), CodingKeys(stringValue: "age", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found number instead.", underlyingError: nil))

解决方案

调整UUIDEmbedded的解码逻辑,优先尝试直接解码目标类型,失败后再尝试字典结构解码,同时扩展KeyedDecodingContainer确保兼容性:

@propertyWrapper
struct UUIDEmbedded<Value: Codable>: Codable {
    var wrappedValue: Value
    
    init(wrappedValue: Value) {
        self.wrappedValue = wrappedValue
    }
    
    enum CodingKeys: String, CodingKey {
        case id
        case value
    }
    
    init(from decoder: Decoder) throws {
        do {
            // 优先尝试直接解码目标类型(适配基础类型的直接值场景)
            self.wrappedValue = try decoder.singleValueContainer().decode(Value.self)
        } catch {
            // 直接解码失败,切换为字典结构解码
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.wrappedValue = try container.decode(Value.self, forKey: .value)
            // 若需保存id,可在此处添加逻辑存储
            // let id = try container.decode(UUID.self, forKey: .id)
        }
    }
    
    func encode(to encoder: Encoder) throws {
        // 编码逻辑可按需调整,示例默认编码为字典结构
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(wrappedValue, forKey: .value)
        // 若存在id,需同步编码
        // try container.encode(id, forKey: .id)
    }
}

// 扩展容器,确保直接值场景下能正确解码为UUIDEmbedded类型
extension KeyedDecodingContainer {
    func decode<T>(_ type: UUIDEmbedded<T>.Type, forKey key: Key) throws -> UUIDEmbedded<T> where T: Codable {
        return try decodeIfPresent(type, forKey: key) ?? UUIDEmbedded(wrappedValue: try decode(T.self, forKey: key))
    }
}

测试验证

测试JSON1(字典结构)

{
    "info": {
        "id": "123e4567-e89b-12d3-a456-426614174000",
        "value": {
            "name": "Alice",
            "age": {
                "id": "987e6543-e21b-43c2-d123-456789012345",
                "value": 30
            },
            "job": {
                "title": "Engineer",
                "years": 5
            }
        }
    }
}

测试JSON2(直接值结构)

{
    "info": {
        "name": "Bob",
        "age": 28,
        "job": {
            "title": "Designer",
            "years": 3
        }
    }
}

解码测试代码

let json1Data = """
{
    "info": {
        "id": "123e4567-e89b-12d3-a456-426614174000",
        "value": {
            "name": "Alice",
            "age": {
                "id": "987e6543-e21b-43c2-d123-456789012345",
                "value": 30
            },
            "job": {
                "title": "Engineer",
                "years": 5
            }
        }
    }
}
""".data(using: .utf8)!

let json2Data = """
{
    "info": {
        "name": "Bob",
        "age": 28,
        "job": {
            "title": "Designer",
            "years": 3
        }
    }
}
""".data(using: .utf8)!

do {
    let employee1 = try JSONDecoder().decode(Employee.self, from: json1Data)
    print("员工1年龄:\(employee1.info.age)") // 输出:员工1年龄:30
    
    let employee2 = try JSONDecoder().decode(Employee.self, from: json2Data)
    print("员工2年龄:\(employee2.info.age)") // 输出:员工2年龄:28
} catch {
    print("解码错误:\(error)")
}

内容的提问来源于stack exchange,提问作者Bluebyte

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 22:53:11