解码JSON时遇到问题。我正在尝试使用
解码我的JSON let temp = try JSONDecoder().decode([LastTemperatureResponse].self, from: data)
。
我的Codable
结构如下:
struct LastTemperatureResponseElement: Codable {
let measurement: Measurement
}
struct Measurement: Codable {
let ts: String
let sensors: [VportSensor]
}
struct VportSensor: TemperatureSensor, Codable {
var lastUpdate: String!
let address, description: String
let status: String
let temperature: Double
}
好吧,如果我正在尝试解码我的JSON,我会收到错误信息
keyNotFound(CodingKeys(stringValue: "status", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "measurement", intValue: nil), CodingKeys(stringValue: "sensors", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"status\", intValue: nil) (\"status\").", underlyingError: nil))
但请查看我的JSON
[
{
"type": "temperatures",
"ts": "2017-11-08T16:43:59.558Z",
"source": "thermo-king",
"unit": {
"number": "1226000743"
},
"measurement": {
"ts": "2017-11-08T16:43:18.000Z",
"sensors": [
{
"address": "t1",
"description": "LFTest1",
"setpoints": [
{
"address": "s1",
"name": "LFSTest1"
}
]
},
{
"address": "t2",
"description": "LFTest2",
"setpoints": [
{
"address": "s2",
"name": "LFSTest2"
}
]
},
{
"address": "t3",
"description": "LFTest3",
"setpoints": [
{
"address": "s3",
"name": "LFSTest3"
}
]
},
{
"address": "t4",
"description": "LFTest4"
},
{
"address": "t5",
"description": "LFTest5"
},
{
"address": "t6",
"description": "LFTest6"
}
],
"sensor": {
"address": "t1",
"name": "LFTest1"
},
"setpoints": [
{
"address": "s1",
"name": "LFSTest1"
}
]
}
},
{
"type": "temperatures",
"ts": "2018-06-07T07:05:38.962Z",
"source": "1-wire",
"unit": {
"number": "1226000743"
},
"measurement": {
"ts": "2018-06-07T07:05:31.000Z",
"sensors": [
{
"address": "2839A5B104000004",
"description": "1-wire #1",
"status": "ok",
"temperature": 24.8
},
{
"address": "28EFBAB104000061",
"description": "1-wire #3",
"status": "ok",
"temperature": 24.5
},
{
"address": "2845F6B504000034",
"description": "1-wire #2",
"status": "ok",
"temperature": 24.5
}
],
"sensor": {
"address": "2839A5B104000004",
"name": "1-wire #1",
"status": "ok"
},
"temperature": 24.8
}
},
{
"type": "temperatures",
"ts": "2018-06-07T07:11:50.030Z",
"source": "vport",
"unit": {
"number": "1226000743"
},
"measurement": {
"ts": "2018-06-07T07:11:47.000Z",
"sensors": [
{
"address": "1036040010",
"description": "Vport 1-wire",
"status": "high",
"temperature": 26
}
],
"sensor": {
"address": "1036040010",
"name": "Vport 1-wire",
"status": "high"
},
"temperature": 26
}
}
]
所以我可以猜测是因为第一部分数据会给出错误,但是应该省略它还是用剩下的数据生成数据?
答案 0 :(得分:0)
在追踪你的问题之后,我认为有几个问题,首先:
您 NOT 声明选项:
基于附加的json,似乎有一些属性 not 总是存在,例如:
status
=> VportSensor
。temperature
=> Measurement
。temperature
=> VportSensor
。temperature
=> setpoints
。您需要确保声明可能未作为可选内容接收的任何属性。
此外,可编码结构的实现:
实现的结构似乎是不典型的json响应结构,请确保声明您的可编码结构与接收的json结构匹配。
请注意:
lastUpdate
和description
未在VportSensor
中使用。
TemperatureSensor
... 提示强>
在处理日期时(例如ts
),您应该直接将其声明为Date
而不是String
,然后设置方便的dateDecodingStrategy
。在您的情况下,它应该是自定义的,您可以在this answer中找到如何执行此操作。
基于以上描述,有完整的实施:
struct Main: Codable {
let type: String
let ts: Date
let source: String
let unit: Unit
let measurement: Measurement
}
struct Unit: Codable {
var number: String
}
struct Measurement: Codable {
let ts: String
let sensors: [VportSensor]
let sensor: VportSensor
let temperature: Double?
}
struct LastTemperatureResponseElement: Codable {
let measurement: Measurement
}
struct VportSensor: Codable {
//let lastUpdate: String!
//let description: String
let address: String
let name: String?
let status: String?
let temperature: Double?
let setpoints: [Setpoint]?
}
struct Setpoint: Codable {
let address: String
let name: String
}
// this part from the mentioned answer for creating custom `dateDecodingStrategy`:
enum DateError: String, Error {
case invalidDate
}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
let container = try decoder.singleValueContainer()
let dateStr = try container.decode(String.self)
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
if let date = formatter.date(from: dateStr) {
return date
}
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
if let date = formatter.date(from: dateStr) {
return date
}
throw DateError.invalidDate
})
<强>输出:强>
let decoder = JSONDecoder()
do {
let temp = try decoder.decode([Main].self, from: json)
// here we go, `temp` is an array of main object of the json
} catch {
print(error)
}
如果您想知道
中的json
是什么
let temp = try decoder.decode([Main].self, from: json)
我刚刚接受了附加的json响应并将其添加到Data
Object:
let json = """
[
{
"type": "temperatures",
"ts": "2017-11-08T16:43:59.558Z",
"source": "thermo-king",
"unit": {
"number": "1226000743"
},
"measurement": {
"ts": "2017-11-08T16:43:18.000Z",
"sensors": [
{
"address": "t1",
"description": "LFTest1",
"setpoints": [
{
"address": "s1",
"name": "LFSTest1"
}
]
},
{
"address": "t2",
"description": "LFTest2",
"setpoints": [
{
"address": "s2",
"name": "LFSTest2"
}
]
},
{
"address": "t3",
"description": "LFTest3",
"setpoints": [
{
"address": "s3",
"name": "LFSTest3"
}
]
},
{
"address": "t4",
"description": "LFTest4"
},
{
"address": "t5",
"description": "LFTest5"
},
{
"address": "t6",
"description": "LFTest6"
}
],
"sensor": {
"address": "t1",
"name": "LFTest1"
},
"setpoints": [
{
"address": "s1",
"name": "LFSTest1"
}
]
}
},
{
"type": "temperatures",
"ts": "2018-06-07T07:05:38.962Z",
"source": "1-wire",
"unit": {
"number": "1226000743"
},
"measurement": {
"ts": "2018-06-07T07:05:31.000Z",
"sensors": [
{
"address": "2839A5B104000004",
"description": "1-wire #1",
"status": "ok",
"temperature": 24.8
},
{
"address": "28EFBAB104000061",
"description": "1-wire #3",
"status": "ok",
"temperature": 24.5
},
{
"address": "2845F6B504000034",
"description": "1-wire #2",
"status": "ok",
"temperature": 24.5
}
],
"sensor": {
"address": "2839A5B104000004",
"name": "1-wire #1",
"status": "ok"
},
"temperature": 24.8
}
},
{
"type": "temperatures",
"ts": "2018-06-07T07:11:50.030Z",
"source": "vport",
"unit": {
"number": "1226000743"
},
"measurement": {
"ts": "2018-06-07T07:11:47.000Z",
"sensors": [
{
"address": "1036040010",
"description": "Vport 1-wire",
"status": "high",
"temperature": 26
}
],
"sensor": {
"address": "1036040010",
"name": "Vport 1-wire",
"status": "high"
},
"temperature": 26
}
}
]
""".data(using: .utf8)!
答案 1 :(得分:0)
您可以轻松跳过服务器响应中未提供的键。
Example JSON response is:
{
"isValid": false,
"pendingAttempts": 2
}
在此json响应中,缺少“ id”字段,并且在我们的代码中我们已经声明了它。因此,我们可以通过以下代码轻松跳过它。
//Code example
struct ResponseModel: Codable {
var id: String? //misng in response
var isValid: Bool?
var token: String?
//initializer
init(id: String?, isValid: Bool?, token: String?) {
self.id = id
self.isValid = isValid
self.token = token
}
//definging the coding keys
enum ResponseModelCodingKeys: String, CodingKey {
//The right hand side keys should be same as of json response keys
case id = "id"
case isValid = "isValid"
case token = "token"
}
//decoding initializer
init(from decoder: Decoder) throws {
var id: String?
var isValid: Bool?
var token: String?
let container = try decoder.container(keyedBy: ResponseModelCodingKeys.self) // defining our (keyed) container
do {
//if found then map
id = try container.decode(String.self, forKey: .id)
}
catch {
//not found then just set the default value
/******** This case will be executed **********/
id = ""
}
do {
//if found then map
isValid = try container.decode(Bool.self, forKey: .isValid)
}
catch {
//not found then just set the default value
isValid = false
}
do {
//if found then map
token = try container.decode(String.self, forKey: .token)
}
catch {
//not found then just set the default value
token = ""
}
//Initializing the model
self.init(id: id, isValid: isValid, token: token)
}
}
当我们对多个API有共同的响应并且每个API都有一些缺失的键时,此技术很有用。