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

Swift Decodable解析含数字的蛇形命名JSON键失败问题求助

解决Swift JSON解码中带数字的蛇形键匹配问题

看起来你遇到了Swift JSON解码里一个很容易踩的坑——默认的蛇形转驼峰策略在处理包含数字的键时,会把数字后的字母大写,导致和你的模型属性名不匹配。让我们一步步拆解问题,然后给出可行的解决方案。

问题根源

先看错误信息:当你使用convertFromSnakeCase时,解码器尝试寻找v_price_24h对应的模型属性,但默认策略会把v_price_24h转换成**vPrice24H**(注意最后一个H是大写的),而你的模型里属性是vPrice24h(h小写),两者不匹配,所以抛出keyNotFound错误。

另外,你在InsideModel里定义了CodingKeys,同时又设置了全局keyDecodingStrategy,这会导致冲突——CodingKeys的优先级高于全局解码策略,解码器会直接用你指定的vPrice24h = "v_price_24h",但因为开启了convertFromSnakeCase,它又会把模型属性名vPrice24h反向转换成蛇形键v_price_24_h,自然找不到对应的值。

解决方案

这里有三个可行的方案,你可以根据自己的需求选择:

方案1:调整模型属性名匹配默认策略的转换结果

最简单的办法是把模型里的vPrice24h改成vPrice24H,这样默认的convertFromSnakeCase就能完美匹配v_price_24h:

import Foundation

var json = """
{ "result": [ { "symbol": "A", "v_price_24h": "39652.50" } ], "time_now": "1645931312.379455" }
"""

struct TopModel: Decodable {
    let timeNow: String
    let result: [InsideModel]
}

struct InsideModel: Decodable {
    let symbol: String
    let vPrice24H: String // 注意这里H大写
}

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

do {
    let model = try decoder.decode(TopModel.self, from: json.data(using: .utf8)!)
    print(model.result[0].vPrice24H) // 输出39652.50
} catch let thrownError {
    print("🚀", thrownError)
}

方案2:自定义key解码策略,精准处理数字后的字母

如果你不想修改模型属性名,可以自定义一个解码策略,确保数字后的字母保持小写。下面是修正后的自定义策略:

import Foundation

var json = """
{ "result": [ { "symbol": "A", "v_price_24h": "39652.50" } ], "time_now": "1645931312.379455" }
"""

struct TopModel: Decodable {
    let timeNow: String
    let result: [InsideModel]
}

struct InsideModel: Decodable {
    let symbol: String
    let vPrice24h: String // 保持原属性名
}

let decoderCustom = JSONDecoder()
decoderCustom.keyDecodingStrategy = .custom { keys in
    let snakeCaseKey = keys.last!.stringValue
    // 分割蛇形键为组件:["v", "price", "24", "h"]
    let components = snakeCaseKey.components(separatedBy: "_")
    // 处理每个组件:第一个组件小写,数字组件保持原样,其他组件首字母大写其余小写
    let camelCaseComponents = components.enumerated().map { index, component in
        if index == 0 {
            return component.lowercased()
        }
        if Int(component) != nil {
            return component
        }
        return component.prefix(1).uppercased() + component.dropFirst().lowercased()
    }
    // 拼接成驼峰键:v + Price + 24 + h → vPrice24h
    let camelCaseKey = camelCaseComponents.joined()
    return CodingKeys(stringValue: camelCaseKey)!
}

do {
    let model = try decoderCustom.decode(TopModel.self, from: json.data(using: .utf8)!)
    print(model.result[0].vPrice24h) // 输出39652.50
} catch let thrownError {
    print("✅", thrownError)
}

方案3:手动指定CodingKeys,绕开自动转换

如果你的键数量不多,也可以直接在模型里定义CodingKeys,同时不要设置全局keyDecodingStrategy(或者保持默认的.useDefaultKeys),这样就能精准映射:

import Foundation

var json = """
{ "result": [ { "symbol": "A", "v_price_24h": "39652.50" } ], "time_now": "1645931312.379455" }
"""

struct TopModel: Decodable {
    enum CodingKeys: String, CodingKey {
        case timeNow = "time_now"
        case result
    }
    let timeNow: String
    let result: [InsideModel]
}

struct InsideModel: Decodable {
    enum CodingKeys: String, CodingKey {
        case symbol
        case vPrice24h = "v_price_24h"
    }
    let symbol: String
    let vPrice24h: String
}

let decoder = JSONDecoder()

do {
    let model = try decoder.decode(TopModel.self, from: json.data(using: .utf8)!)
    print(model.result[0].vPrice24h) // 输出39652.50
} catch let thrownError {
    print("🛑", thrownError)
}

对你现有代码的修正说明

你之前的自定义策略里用了capitalized,它会把每个单词的首字母大写,包括数字后面的"h"变成"H",导致和模型属性vPrice24h不匹配。另外,同时使用CodingKeys和全局keyDecodingStrategy会造成冲突,所以要么去掉CodingKeys,要么不要设置全局策略。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 14:47:34