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

提问人:Micsa Dan 提问时间:2/1/2023 最后编辑:jnpdxMicsa Dan 更新时间:2/1/2023 访问量:204

问:

所以,让我用一个例子来详细说明一下:

假设我想获取有关人员的数据。每个人都有 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 的零值?...但是,在归因值之前,我怎样才能检测到这一点呢?

谢谢你,我随时都在这里!

JSON Swift 解析 可编码

评论

0赞 Micsa Dan 2/1/2023
好的,我会试一试,如果它有效,我会关闭这个案子。同时,任何帮助都是感激的!
0赞 Sulthan 2/1/2023
@MicsaDan 这是一个不好的建议,它不适用于这种情况。通常,您需要一个自定义初始值设定项并显式处理大小写。有多种方法可以做到这一点。例如,您可以定义一个方法,如果对象是空数组,则返回该方法。[]decodeIfPresentnil
0赞 Micsa Dan 2/1/2023
@Sulthan我尝试使用json生成器,但收到相同的错误。如何实现 decodeIfPresent?我以前从未听说过
0赞 Larme 2/1/2023
stackoverflow.com/questions/46292325/......
0赞 Micsa Dan 2/1/2023
@Larme我明白了 Optional 和 decodeIfPresent 之间的区别,但我如何在类中将其用于 Ebitda?

答:

1赞 timbre timbre 2/1/2023 #1

我以前见过这个问题。我相信这是由于 JS 后端,它有时(我真的不知道为什么)发送空数组而不是 null。在更简单的情况下提炼您的问题:似乎您可能会为同一字段获得 2 个不同的值:

let json1 = """
{
   "age": 2
}
""".data(using: .utf8)!

或者,如果 value 为 ,则得到一个空数组:nil

let json2 = """
{
   "age": []
}
""".data(using: .utf8)!

因此,如果您只是将您的定义为:ageInt

struct Obj: Codable {
    let age: Int?
}

它将适用于 ,但会崩溃。json1json2

不幸的是,这意味着您需要切换到“手动解析”此类字段,因此,整个结构:

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

评论

0赞 Micsa Dan 2/2/2023
我会尝试一下,但似乎这是解决方案,所以我会投票作为答案。谢谢!