提问人:Micsa Dan 提问时间:2/1/2023 最后编辑:jnpdxMicsa Dan 更新时间:2/1/2023 访问量:204
Swift 中的 JSON 解析。当 value 不为 nil 时,我可以解析 JSON 数据。如果 value 为 nil,我收到一个空数组而不是 nil
JSON parsing in Swift. When value is not nil, I can parse the JSON data. If value is nil, I receive an empty array instead of nil
问:
所以,让我用一个例子来详细说明一下:
假设我想获取有关人员的数据。每个人都有 3 个系列:朋友、家庭和工作。
Friends 集合包含: Person 对象(描述每个朋友:姓名、年龄和时间)
族集合包含:Person 对象(描述每个族成员:姓名、年龄和时间)
工作集合包含:Person 对象(描述每个工作伙伴:姓名、年龄和时间)
所以它将是这样的:
Me {
age:25,
name: Dan,
Friends [{age:21,name:Andrew}, {age:30,name:Mars}]
Family [{age:21,name:Andrew}, {age:30,name:Mars}]
Work [{age:21,name:Andrew}, {age:30,name:Mars}]
}
现在,记住这个例子。如果我不想指定我的名字,我将使用“age:nil”而不是“age:25”,对吧?因为年龄是一个 Int?,一个 obtional。
我的问题是,当我解析JSON数据时,我没有收到一个nil值(从那个obtional age:Int?),而是收到一个[Int]类型的空数组,所以inteaseed of nil,我收到[]。
现在,这是我的例子:
class FinancialData: Codable {
var maxAge:Int?
var currentPrice:CurrentPrice?
var targetHighPrice:TargetHighPrice?
var targetLowPrice:TargetLowPrice?
var targetMeanPrice:TargetMeanPrice?
var targetMedianPrice:TargetMedianPrice?
var recommendationMean:RecommendationMean?
var recommendationKey, financialCurrency:String?
var numberOfAnalystOpinions:NumberOfAnalystOpinions?
var totalCash:TotalCash?
var totalCashPerShare:TotalCashPerShare?
var ebitda:Ebitda
var totalDebt:TotalDebt?
var quickRatio:QuickRatio?
var currentRatio:CurrentRatio?
var totalRevenue:TotalRevenue?
var debtToEquity:DebtToEquity?
var revenuePerShare:RevenuePerShare?
var returnOnAssets:ReturnOnAssets?
var returnOnEquity:ReturnOnEquity?
var grossProfits:GrossProfits?
var freeCashflow:FreeCashFlow?
var operatingCashflow:OperatingCashFlow?
// var earningsGrowth:EarningsGrowth?
var revenueGrowth:RevenueGrowth?
var grossMargins:GrossMargins?
var ebitdaMargins:EbitdaMargins?
var operatingMargins:OperatingMargins?
var profitMargins:ProfitMargins?
enum CodingKeys: String, CodingKey {
case maxAge = "maxAge"
case currentPrice = "currentPrice"
case targetHighPrice = "targetHighPrice"
case targetLowPrice = "targetLowPrice"
case targetMeanPrice = "targetMeanPrice"
case targetMedianPrice = "targetMedianPrice"
case recommendationMean = "recommendationMean"
case recommendationKey = "recommendationKey"
case numberOfAnalystOpinions = "numberOfAnalystOpinions"
case totalCash = "totalCash"
case totalCashPerShare = "totalCashPerShare"
case ebitda = "ebitda"
case totalDebt = "totalDebt"
case quickRatio = "quickRatio"
case currentRatio = "currentRatio"
case totalRevenue = "totalRevenue"
case debtToEquity = "debtToEquity"
case revenuePerShare = "revenuePerShare"
case returnOnAssets = "returnOnAssets"
case returnOnEquity = "returnOnEquity"
case grossProfits = "grossProfits"
case freeCashflow = "freeCashflow"
case operatingCashflow = "operatingCashflow"
// case earningsGrowth = "earningsGrowth"
case revenueGrowth = "revenueGrowth"
case grossMargins = "grossMargins"
case ebitdaMargins = "ebitdaMargins"
case operatingMargins = "operatingMargins"
case profitMargins = "profitMargins"
case financialCurrency = "financialCurrency"
}
}
//....//
class Ebitda: Codable {
var raw:Double?
var fmt, longFmt: String?
enum CodingKeys: String, CodingKey {
case raw = "raw"
case fmt = "fmt"
case longFmt = "longFmt"
}
}
EBITDA是问题所在,是一个类。如果我的 API 中没有 Ebitda 类,我没有收到 Ebitda? 的 Obtional nil 值,我收到的是一个空的 Ebitda 数组,并且我的程序崩溃了。我无法修改 Ebitda?更改为 [Ebitda],因为当该值不为零时,我收到的是一个 Ebitda 对象,而不是 Ebitda 对象数组。
因此,当 Ebitda 为 nil 时,这是错误,但我收到一个空数组而不是 nil:
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: “financialData”, intValue: nil), CodingKeys(stringValue: “ebitda”, intValue: nil)], debugDescription: “应该解码 Dictionary<String, Any> 但找到了一个数组。
我将附上我导入的 JSON 数据的简短描述,查找 ebitda:
{
"financialData": {
"maxAge": 86400,
"recommendationKey": "buy",
"numberOfAnalystOpinions": {
"raw": 23,
"fmt": "23",
"longFmt": "23"
},
"totalCash": {
"raw": 848978968576,
"fmt": "848.98B",
"longFmt": "848,978,968,576"
},
"totalCashPerShare": {
"raw": 106.165,
"fmt": "106.17"
},
"ebitda": [],
"totalDebt": {
"raw": 543510003712,
"fmt": "543.51B",
"longFmt": "543,510,003,712"
},
"quickRatio": [],
"currentRatio": [],
"debtToEquity": [],
"revenuePerShare": {
"raw": 11.389,
"fmt": "11.39"
},
"returnOnAssets": {
"raw": 0.00885,
"fmt": "0.88%"
},
"returnOnEquity": {
"raw": 0.101339996,
"fmt": "10.13%"
},
"grossProfits": {
"raw": 92407000000,
"fmt": "92.41B",
"longFmt": "92,407,000,000"
},
"freeCashflow": [],
"operatingCashflow": [],
"earningsGrowth": {
"raw": 0.069,
"fmt": "6.90%"
},
"revenueGrowth": {
"raw": 0.04,
"fmt": "4.00%"
},
"grossMargins": {
"raw": 0,
"fmt": "0.00%"
},
"ebitdaMargins": {
"raw": 0,
"fmt": "0.00%"
},
"operatingMargins": {
"raw": 0.33514,
"fmt": "33.51%"
},
"profitMargins": {
"raw": 0.29790002,
"fmt": "29.79%"
},
"financialCurrency": "USD"
}
}
如您所见,quickRatio 和 currentRatio 也是无类型的空数组,而不是 CurrentRatio 对象 nil。我能做些什么来解决这个问题?谢谢!
我正在考虑计算属性,但它不起作用。在将数据归因于我的变量之前,有没有办法在解析数据时检测值是否为零?如果我能做到这一点,我将能够(对于 EBITDA 和类似情况):
如果该值不是 nil(在我的情况下不是空数组),则从 json 数据中归属于 ebitda 变量 Ebitda 对象。
如果值为 nil(在我的情况下是一个无类型的空数组)以归因于 ebitda 值,则 ebitda 值为 nil 值?对象。
我认为这就是解决方案,我不能归因于 ebitda:Ebitda?一个数组。.我必须贡献 Ebitda 的零值?...但是,在归因值之前,我怎样才能检测到这一点呢?
谢谢你,我随时都在这里!
答:
我以前见过这个问题。我相信这是由于 JS 后端,它有时(我真的不知道为什么)发送空数组而不是 null。在更简单的情况下提炼您的问题:似乎您可能会为同一字段获得 2 个不同的值:
let json1 = """
{
"age": 2
}
""".data(using: .utf8)!
或者,如果 value 为 ,则得到一个空数组:nil
let json2 = """
{
"age": []
}
""".data(using: .utf8)!
因此,如果您只是将您的定义为:age
Int
struct Obj: Codable {
let age: Int?
}
它将适用于 ,但会崩溃。json1
json2
不幸的是,这意味着您需要切换到“手动解析”此类字段,因此,整个结构:
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Keys.self)
age = try? container.decode(Int.self, forKey: .age)
// parse every field in the structure
}
但是,如果你的结构中有很多字段,它就会变得非常乏味。因此,一个捷径是定义一个为您解析它的结构:
struct ValueOrEmptyArray<T: Codable>: Codable {
let value: T?
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
value = try container.decode(T.self) // try to parse
} catch {
value = nil // failed parsing - assume it's nil
}
}
}
因此,您可以在结构中使用它,而无需对每个字段进行解析:
struct Obj: Codable {
let age: ValueOrEmptyArray<Int>
}
当然,这也意味着要访问此类字段的值,您需要访问:age.value
let decoded1 = try JSONDecoder().decode(Obj.self, from: json1)
print(decoded1.age.value) // 2
let decoded2 = try JSONDecoder().decode(Obj.self, from: json2) // throws an exception
print(decoded2.age.value) // nil
评论
[]
decodeIfPresent
nil