如何在 Swift 中使用 Decodable 解析 JSON 元素。我这样做时收到错误
Posted
技术标签:
【中文标题】如何在 Swift 中使用 Decodable 解析 JSON 元素。我这样做时收到错误【英文标题】:How to parse JSON Element with Decodable in Swift. I receive errors when doing so 【发布时间】:2020-11-24 00:05:12 【问题描述】:
"optionChain":
"result": [
"underlyingSymbol": "AAPL",
"expirationDates": [
1606435200,
1655424000,
1663286400,
1674172800
],
"strikes": [
55,
60,
65,
70,
75,
80,
85,
90,
95,
96,
97,
98,
99,
100,
101,
102,
103,
104,
105,
106,
107,
108,
109,
110,
111,
112,
113,
114,
115,
116,
131,
132,
133,
155,
160,
165,
170,
175
],
"hasMiniOptions": false,
"quote":
"language": "en-US",
"region": "US",
"quoteType": "EQUITY",
"quoteSourceName": "Nasdaq Real Time Price",
"triggerable": true,
"currency": "USD",
"firstTradeDateMilliseconds": 345479400000,
"priceHint": 2,
"regularMarketChange": -2.2599945,
"regularMarketChangePercent": -1.9260223,
"regularMarketTime": 1606152628,
"regularMarketPrice": 115.08,
"regularMarketDayHigh": 117.62,
"regularMarketDayRange": "113.8 - 117.62",
"regularMarketDayLow": 113.8,
"regularMarketVolume": 66403356,
"regularMarketPreviousClose": 117.34,
"bid": 114.79,
"ask": 114.78,
"bidSize": 9,
"askSize": 10,
"fullExchangeName": "NasdaqGS",
"financialCurrency": "USD",
"regularMarketOpen": 117.18,
"averageDailyVolume3Month": 150914918,
"averageDailyVolume10Day": 78477066,
"fiftyTwoWeekLowChange": 61.9275,
"fiftyTwoWeekLowChangePercent": 1.165091,
"fiftyTwoWeekRange": "53.1525 - 137.98",
"fiftyTwoWeekHighChange": -22.899994,
"fiftyTwoWeekHighChangePercent": -0.16596605,
"fiftyTwoWeekLow": 53.1525,
"fiftyTwoWeekHigh": 137.98,
"dividendDate": 1605139200,
"earningsTimestamp": 1603989000,
"earningsTimestampStart": 1611658740,
"earningsTimestampEnd": 1612180800,
"trailingAnnualDividendRate": 0.795,
"trailingPE": 35.085365,
"trailingAnnualDividendYield": 0.0067751836,
"epsTrailingTwelveMonths": 3.28,
"epsForward": 4.33,
"epsCurrentYear": 3.96,
"priceEpsCurrentYear": 29.060606,
"sharesOutstanding": 17102499840,
"bookValue": 3.849,
"fiftyDayAverage": 116.774574,
"fiftyDayAverageChange": -1.6945724,
"fiftyDayAverageChangePercent": -0.014511485,
"twoHundredDayAverage": 104.00763,
"twoHundredDayAverageChange": 11.072372,
"twoHundredDayAverageChangePercent": 0.10645731,
"marketCap": 1956567187456,
"forwardPE": 26.577368,
"priceToBook": 29.898676,
"sourceInterval": 15,
"exchangeDataDelayedBy": 0,
"tradeable": false,
"exchange": "NMS",
"shortName": "Apple Inc.",
"longName": "Apple Inc.",
"marketState": "REGULAR",
"messageBoardId": "finmb_24937",
"exchangeTimezoneName": "America/New_York",
"exchangeTimezoneShortName": "EST",
"gmtOffSetMilliseconds": -18000000,
"market": "us_market",
"esgPopulated": false,
"displayName": "Apple",
"symbol": "AAPL"
,
"options": [
"expirationDate": 1606435200,
"hasMiniOptions": false,
"calls": [
"contractSymbol": "AAPL201127C00165000",
"strike": 165,
"currency": "USD",
"lastPrice": 0.01,
"change": 0,
"percentChange": 0,
"volume": 1,
"openInterest": 156,
"bid": 0,
"ask": 0.01,
"contractSize": "REGULAR",
"expiration": 1606435200,
"lastTradeDate": 1605731100,
"impliedVolatility": 1.000005,
"inTheMoney": false
,
"contractSymbol": "AAPL201127C00170000",
"strike": 170,
"currency": "USD",
"lastPrice": 0.01,
"change": 0,
"percentChange": 0,
"volume": 11,
"openInterest": 353,
"bid": 0,
"ask": 0.01,
"contractSize": "REGULAR",
"expiration": 1606435200,
"lastTradeDate": 1605731115,
"impliedVolatility": 1.0625046875000002,
"inTheMoney": false
,
"contractSymbol": "AAPL201127C00175000",
"strike": 175,
"currency": "USD",
"lastPrice": 0.01,
"change": 0,
"percentChange": 0,
"volume": 5,
"openInterest": 259,
"bid": 0,
"ask": 0.01,
"contractSize": "REGULAR",
"expiration": 1606435200,
"lastTradeDate": 1606150652,
"impliedVolatility": 1.15625421875,
"inTheMoney": false
],
"puts": [
"contractSymbol": "AAPL201127P00134000",
"strike": 134,
"currency": "USD",
"lastPrice": 14.2,
"change": 0,
"percentChange": 0,
"volume": 4,
"openInterest": 133,
"bid": 19.1,
"ask": 19.35,
"contractSize": "REGULAR",
"expiration": 1606435200,
"lastTradeDate": 1605632020,
"impliedVolatility": 0.831056376953125,
"inTheMoney": true
,
"contractSymbol": "AAPL201127P00135000",
"strike": 135,
"currency": "USD",
"lastPrice": 21.05,
"change": 3.869999,
"percentChange": 22.526186,
"volume": 38,
"openInterest": 258,
"bid": 20.35,
"ask": 20.5,
"contractSize": "REGULAR",
"expiration": 1606435200,
"lastTradeDate": 1606148156,
"impliedVolatility": 0.96679720703125,
"inTheMoney": true
,
"contractSymbol": "AAPL201127P00138000",
"strike": 138,
"currency": "USD",
"lastPrice": 19.41,
"change": 0,
"percentChange": 0,
"openInterest": 1,
"bid": 23.1,
"ask": 23.25,
"contractSize": "REGULAR",
"expiration": 1606435200,
"lastTradeDate": 1605819053,
"impliedVolatility": 0.91992267578125,
"inTheMoney": true
,
"contractSymbol": "AAPL201127P00140000",
"strike": 140,
"currency": "USD",
"lastPrice": 21.19,
"change": 0,
"percentChange": 0,
"volume": 24,
"openInterest": 53,
"bid": 25.2,
"ask": 25.35,
"contractSize": "REGULAR",
"expiration": 1606435200,
"lastTradeDate": 1605818102,
"impliedVolatility": 1.0429735351562504,
"inTheMoney": true
,
"contractSymbol": "AAPL201127P00145000",
"strike": 145,
"currency": "USD",
"lastPrice": 26.85,
"change": 0,
"percentChange": 0,
"volume": 1,
"openInterest": 55,
"bid": 30.2,
"ask": 30.35,
"contractSize": "REGULAR",
"expiration": 1606435200,
"lastTradeDate": 1605892788,
"impliedVolatility": 1.185550947265625,
"inTheMoney": true
,
"contractSymbol": "AAPL201127P00175000",
"strike": 175,
"currency": "USD",
"lastPrice": 57.13,
"change": 0,
"percentChange": 0,
"volume": 2,
"openInterest": 2,
"bid": 60.15,
"ask": 60.25,
"contractSize": "REGULAR",
"expiration": 1606435200,
"lastTradeDate": 1605798274,
"impliedVolatility": 1.822266513671875,
"inTheMoney": true
]
]
],
"error": null
以上是我调用的 API JSON 响应。现在下面是用 swift 编写的可解码。
struct Something: Decodable
let optionChain: OptionChain
let error: String
struct OptionChain: Decodable
let result: [ResultElement]
struct ResultElement: Decodable
let underlyingSymbol: String
let expirationDates: [Int]
let strikes: [Int]
let hasMiniOptions: Bool
let quote: [quoteElement]
let options: [optionsElement]
struct quoteElement: Decodable
let language: String
let region: String
let quoteType: String
let quoteSourceName: String
let triggerable: Bool
let currency: String
let firstTradeDateMilliseconds: Int
let priceHint: Int
let regularMarketChange: Int
let regularMarketChangePercent: Int
let regularMarketTime: Int
let regularMarketPrice: Int
let regularMarketDayHigh: Int
let regularMarketDayRange: String
let regularMarketDayLow: Int
let regularMarketVolume: Int
let regularMarketPreviousClose: Int
let bid: Int
let ask: Int
let bidSize: Int
let askSize: Int
let fullExchangeName: String
let financialCurrency: String
let regularMarketOpen: Int
let averageDailyVolume3Month: Int
let averageDailyVolume10Day: Int
let fiftyTwoWeekLowChange: Int
let fiftyTwoWeekLowChangePercent: Int
let fiftyTwoWeekRange: String
let fiftyTwoWeekHighChange: Int
let fiftyTwoWeekHighChangePercent: Int
let fiftyTwoWeekLow: Int
let fiftyTwoWeekHigh: Int
let dividendDate: Int
let earningsTimestamp: Int
let earningsTimestampStart: Int
let earningsTimestampEnd: Int
let trailingAnnualDividendRate: Int
let trailingPE: Int
let trailingAnnualDividendYield: Int
let epsTrailingTwelveMonths: Int
let epsForward: Int
let epsCurrentYear: Int
let priceEpsCurrentYear: Int
let sharesOutstanding: Int
let bookValue: Int
let fiftyDayAverage: Int
let fiftyDayAverageChange: Int
let fiftyDayAverageChangePercent: Int
let twoHundredDayAverage: Int
let twoHundredDayAverageChange: Int
let twoHundredDayAverageChangePercent: Int
let marketCap: Int
let forwardPE: Int
let priceToBook: Int
let sourceInterval: Int
let exchangeDataDelayedBy: Int
let tradeable: Bool
let exchange: String
let shortName: String
let longName: String
let marketState: String
let messageBoardId: String
let exchangeTimezoneName: String
let exchangeTimezoneShortName: String
let gmtOffSetMilliseconds: Int
let market: String
let esgPopulated: Bool
let displayName: String
let symbol: String
struct optionsElement: Decodable
let expirationDate: Int
let hasMiniOptions: Bool
let calls: [callPutElement]
let puts: [callPutElement]
struct callPutElement: Decodable
let contractSymbol: String
let strike: Int
let currency: String
let lastPrice: Int
let change: Int
let percentChange: Int
let volume: Int
let openInterest: Int
let bid: Int
let ask: Int
let contractSize: String
let expiration: Int
let lastTradeDate: Int
let impliedVolatility: Int
let inTheMoney: Bool
我从下面的代码中调用这些可解码:
let request = NSMutableURLRequest(url: NSURL(string: urlTicker)! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: (data, response, error) -> Void in
if (error != nil)
print(error)
else
let httpResponse = response as? HTTPURLResponse
do
let jsonString = "..."
let jsonData = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let something = try decoder.decode(Something.self, from: jsonData)
print(something.optionChain.result.map $0.underlyingSymbol )
//let myJSON = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any]
//let likes = myJSON!["optionChain"] as? Array<Dictionary<String, Any>>
catch
print("JSONSerialization error:", error)
//print(httpResponse)
//print(myJSON["strikes"] as! String)
)
dataTask.resume()
我进入 catch 语句,错误是:
SONSerialization 错误: dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "No string key for value in字符 1 周围的对象。" UserInfo=NSDebugDescription=字符 1 周围对象中的值没有字符串键。)))
我是不是用错了解码器?
【问题讨论】:
正如错误所说,您的 JSON 无效。最后似乎缺少
。
let something = try decoder.decode(Something.self, from: data!)
那个 一直都在,我忘记在论坛里加了。嗯,你还能想到什么?
@LeoDabus 我尝试了那行代码,但现在我收到了这个错误。 JSONSerialization 错误:typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "optionChain", intValue: nil), CodingKeys(stringValue: "result", intValue: nil), _JSONKey(stringValue : "Index 0", intValue: 0), CodingKeys(stringValue: "quote", intValue: nil)], debugDescription: "期望解码 Array 但找到了字典。",底层错误: nil))跨度>
wowww 不知道那是一回事。我会试试这个!谢谢!
【参考方案1】:
解码您的数据时我必须注意的唯一问题是设置日期解码策略。只需将您的 dateDecodingStrategy 设置为 .secondsSince1970
。试试这样:
struct Root: Codable
let optionChain: OptionChain
struct OptionChain: Codable
let result: [Result]
struct Result: Codable
let underlyingSymbol: String
let expirationDates: [Date]
let strikes: [Int]
let hasMiniOptions: Bool
let quote: Quote
let options: [Option]
struct Option: Codable
let expirationDate: Date
let hasMiniOptions: Bool
let calls: [Call]
let puts: [Call]
struct Call: Codable
let contractSymbol: String
let strike: Int
let currency: String
let lastPrice: Double
let change, percentChange: Double
let volume: Int?
let openInterest: Int
let bid, ask: Double
let contractSize: String
let expiration, lastTradeDate: Date
let impliedVolatility: Double
let inTheMoney: Bool
struct Quote: Codable
let language,region, quoteType, quoteSourceName: String
let triggerable: Bool
let currency: String
let firstTradeDateMilliseconds, priceHint: Int
let regularMarketChange, regularMarketChangePercent: Double
let regularMarketTime: Int
let regularMarketPrice, regularMarketDayHigh: Double
let regularMarketDayRange: String
let regularMarketDayLow: Double
let regularMarketVolume: Int
let regularMarketPreviousClose, bid, ask: Double
let bidSize, askSize: Int
let fullExchangeName, financialCurrency: String
let regularMarketOpen: Double
let averageDailyVolume3Month, averageDailyVolume10Day: Int
let fiftyTwoWeekLowChange, fiftyTwoWeekLowChangePercent: Double
let fiftyTwoWeekRange: String
let fiftyTwoWeekHighChange, fiftyTwoWeekHighChangePercent: Double
let fiftyTwoWeekLow, fiftyTwoWeekHigh: Double
let dividendDate, earningsTimestamp, earningsTimestampStart, earningsTimestampEnd: Date
let trailingAnnualDividendRate, trailingPE, trailingAnnualDividendYield, epsTrailingTwelveMonths, epsForward, epsCurrentYear, priceEpsCurrentYear: Double
let sharesOutstanding: Int
let bookValue, fiftyDayAverage, fiftyDayAverageChange, fiftyDayAverageChangePercent, twoHundredDayAverage, twoHundredDayAverageChange, twoHundredDayAverageChangePercent: Double
let marketCap: Int
let forwardPE, priceToBook: Double
let sourceInterval, exchangeDataDelayedBy: Int
let tradeable: Bool
let exchange, shortName, longName, marketState, messageBoardId, exchangeTimezoneName, exchangeTimezoneShortName: String
let gmtOffSetMilliseconds: Int
let market: String
let esgPopulated: Bool
let displayName, symbol: String
游乐场测试
do
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
let root = try decoder.decode(Root.self, from: .init(json.utf8))
print(root)
catch
print(error)
这将打印出来
Root(optionChain: __lldb_expr_184.OptionChain(result: [__lldb_expr_184.Result(underlyingSymbol: "AAPL", expirationDates: [2020-11-27 00:00:00 +0000, 2022-06-17 00:00:00 +0000, 2022-09-16 00:00:00 +0000, 2023-01-20 00:00:00 +0000],罢工:[55, 60, 65, 70, 75, 80, 85, 90, 95 , 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 131, 132, 133, 155 , 160, 165, 170, 175], hasMiniOptions: false, quote: __lldb_expr_184.Quote(language: "en-US", region: "US", quoteType: "EQUITY", quoteSourceName: "Nasdaq Real Time Price", 可触发:真实,货币:“美元”,FirstTraDedateMilliseconds:345479400000,Presshint:2,ramentmarkange:-2.2599945,ramentmarketchangepercent:-1.9260223,promistmarkettime:1606152628,prossionmarketprice:115.08,正则马克赛特·星期六:117.62,ramentMarketdayRange:“113.8 - 117.62”,RondalmmarketDayLow:113.8 ,regularMarketVolume:66403356,regularMarketPreviousClose:117.34,bid:114.79,ask:114.78,bidSize:9,a skSize:10,fullExchangeName: “纳斯达克交易代码”,financialCurrency: “USD”,regularMarketOpen:117.18,averageDailyVolume3Month:150914918,averageDailyVolume10Day:78477066,fiftyTwoWeekLowChange:61.9275,fiftyTwoWeekLowChangePercent:1.165091,fiftyTwoWeekRange: “53.1525 - 137.98”,fiftyTwoWeekHighChange:-22.899994,fiftyTwoWeekHighChangePercent :-0.16596605,fiftyTwoWeekLow:53.1525,fiftyTwoWeekHigh:137.98,股息日期:2020-11-12 00:00:00 +0000,收益时间戳:2020-10-29 16:30:00 +0000,收益时间戳开始:2021-01-26 1 :59:00 +0000,egerningStimeStampend:2021-02-01 12:00:00 +0000,TrailingannualdIvidendrate:0.795,尾部:35.085365,trailingannualdidendyield:0.0067751836,epstrailingtwelvemonths:3.28,epsforward:4.33,epcurrental:3.96,priceepcurrental:29.060606, shareOutstanding:17102499840,账面价值:3.849,50DayAverage:116.774574,50DayAverageChange:-1.6945724,50DayAverageChangePercent:-0.014511485,twoHundredDayAverage:104.00763,twoHundr edDayAverageChange: 11.072372, twoHundredDayAverageChangePercent: 0.10645731, marketCap: 1956567187456, forwardPE: 26.577368, priceToBook: 29.898676, sourceInterval: 15, exchangeDataDelayedBy: 0, tradeable: false, exchange: "NMS", shortName: "longApple Inc." Inc.”,marketState:“REGULAR”,messageBoardId:“finmb_24937”,exchangeTimezoneName:“America/New_York”,exchangeTimezoneShortName:“EST”,gmtOffSetMilliseconds:-18000000,market:“us_market”,esgPopulated:false,displayName:“Apple” , 符号: "AAPL"), 选项: [__lldb_expr_184.Option(expirationDate: 2020-11-27 00:00:00 +0000, hasMiniOptions: false, 调用: [__lldb_expr_184.Call(contractSymbol: "AAPL201127C00165000", 罢工: 165 ,货币:“USD”,lastPrice:0.01,change:0.0,percentChange:0.0,volume:Optional(1),openInterest:156,bid:0.0,ask:0.01,contractSize:“REGULAR”,到期:2020-11- 27 00:00:00 +0000, lastTradeDate: 2020-11-18 20:25:00 +0000, 隐含波动率: 1.000005, inTheMoney: false), __lldb_expr _184.Call(contractSymbol: "AAPL201127C00170000", 行使价: 170, 货币: "USD", lastPrice: 0.01, change: 0.0, percentChange: 0.0, volume: Optional(11), openInterest: 353, bid: 0.0, ask: 0.01 ,contractSize:“常规”,到期:2020-11-27 00:00:00 +0000,lastTradeDate:2020-11-18 20:25:15 +0000,隐含波动率:1.0625046875000002,inTheMoney:false),__lldb_expr_184.Call( contractSymbol:“AAPL201127C00175000”,行使价:175,货币:“USD”,lastPrice:0.01,change:0.0,percentChange:0.0,volume:Optional(5),openInterest:259,bid:0.0,ask:0.01,contractSize:“常规”,到期:2020-11-27 00:00:00 +0000,lastTradeDate:2020-11-23 16:57:32 +0000,隐含波动率:1.15625421875,inTheMoney:false)],看跌期权:[__lldb_expr_184.Call( contractSymbol:“AAPL201127P00134000”,行使价:134,货币:“USD”,lastPrice:14.2,change:0.0,percentChange:0.0,volume:Optional(4),openInterest:133,bid:19.1,ask:19.35,contractSize:“常规”,到期时间:2020-11-27 00:00:00 +0000,lastTrad eDate: 2020-11-17 16:53:40 +0000, 隐含波动率: 0.831056376953125, inTheMoney: true), __lldb_expr_184.Call(contractSymbol: "AAPL201127P00135000", 行使价: 135, 货币: "USD", lastPrice: 21.05, change: 3.869999, percentChange: 22.526186, volume: Optional(38), openInterest: 258, bid: 20.35, ask: 20.5, contractSize: "REGULAR", expiration: 2020-11-27 00:00:00 +0000, lastTradeDate: 2020- 11-23 16:15:56 +0000, 隐含波动率: 0.96679720703125, inTheMoney: true), __lldb_expr_184.Call(contractSymbol: "AAPL201127P00138000", 罢工: 138, 货币: "USD", lastPrice: 19.41, change: 0.0, percentChange: 0.0,交易量:nil,openInterest:1,bid:23.1,ask:23.25,contractSize:“REGULAR”,到期:2020-11-27 00:00:00 +0000,lastTradeDate:2020-11-19 20:50: 53 +0000, 隐含波动率: 0.91992267578125, inTheMoney: true), __lldb_expr_184.Call(contractSymbol: "AAPL201127P00140000", 执行价: 140, 货币: "USD", lastPrice: 21.19, change: 0.0, percentChange: 0.0, volume: Optional(24 ), 打开兴趣: 53, bid: 25.2, ask: 25.35, contractSize: "REGULAR", 到期: 2020-11-27 00:00:00 +0000, lastTradeDate: 2020-11-19 20:35:02 +0000, 隐含波动率: 1.0429735351562504 , inTheMoney: true), __lldb_expr_184.Call(contractSymbol: "AAPL201127P00145000", 行使价: 145, 货币: "USD", lastPrice: 26.85, change: 0.0, percentChange: 0.0, volume: Optional(1), openInterest: 55, bid : 30.2, ask: 30.35, contractSize: "REGULAR", expiration: 2020-11-27 00:00:00 +0000, lastTradeDate: 2020-11-20 17:19:48 +0000, 隐含波动率: 1.185550947265625, inTheMoney: true ), __lldb_expr_184.Call(contractSymbol: "AAPL201127P00175000", 行使价: 175, 货币: "USD", lastPrice: 57.13, change: 0.0, percentChange: 0.0, volume: Optional(2), openInterest: 2, bid: 60.15, ask :60.25,contractSize:“常规”,到期:2020-11-27 00:00:00 +0000,lastTradeDate:2020-11-19 15:04:34 +0000,隐含波动率:1.822266513671875,inTheMoney:true)])] )]))
【讨论】:
太棒了。谢谢!以上是关于如何在 Swift 中使用 Decodable 解析 JSON 元素。我这样做时收到错误的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Swift 4 中将 Encodable 或 Decodable 作为参数传递?
在 Swift 4 中使用 Decodable 和 CodingKeys 解析 JSON
如何使用 Swift 的 Decodable 解析任意 JSON 字符串,而您只知道或关心几个字段? [关闭]