解码期间未从其他结构中找到 CodingKey
CodingKey not found from a different struct during decoding
我正在尝试使用来自 Datum
结构的已解析日期字符串 (dateString
) 在 AzimuthAtDate
class...
{
"11": [{
"name": "Sun",
"date": "2021-01-26",
"rising": [{
"coord": "113.6681",
"hour": "07:16"
}],
"transit-sup": [{
"coord": "32.4711",
"hour": "12:16"
}],
"setting": [{
"coord": "246.4743",
"hour": "17:17"
}]
}]
}
struct Datum: Codable, CustomStringConvertible {
let name: String
let dateString: String
let isoDate: Date?
let rising: [AzimuthAtDate]
let transit: [ElevationAtDate]
let setting: [AzimuthAtDate]
enum CodingKeys: String, CodingKey {
case name
case dateString = "date"
case isoDate
case rising = "rising"
case transit = "transit-sup"
case setting = "setting"
}
init(from decoder: Decoder) throws {
let con = try decoder.container(keyedBy: CodingKeys.self)
self.name = try con.decode(String.self, forKey: .name)
self.dateString = try con.decode(String.self, forKey: .dateString)
self.isoDate = try con.decode(Date.self, forKey: .dateString)
self.rising = decodeAzimuthAtDate(decoder: decoder, key: .rising)
self.transit = decodeElevationAtDate(decoder: decoder, key: .transit)
self.setting = decodeAzimuthAtDate(decoder: decoder, key: .setting)
}
}
class CoordHour: Codable {
let coord: String
let hour: String
var isoDate: Date?
init(coord: String, hour: String, isoDate: Date?) {
self.coord = coord
self.hour = hour
self.isoDate = isoDate
}
}
但是在尝试从 AzimuthAtDate
中访问 Datum.CodingKeys
的密钥时,datumContainer
没有密钥。所以我得到这样的错误:
keyNotFound(CodingKeys(stringValue: "date", intValue: nil) ... debugDescription: "No value associated with key CodingKeys(stringValue: \"date\", intValue: nil) (\"date\").", underlyingError: nil))
class AzimuthAtDate: CoordHour {
required init(from decoder: Decoder) throws {
let coordHourContainer = try decoder.container(keyedBy: CoordHour.CodingKeys.self)
let coord = try coordHourContainer.decode(String.self, forKey: .coord)
let hour = try coordHourContainer.decode(String.self, forKey: .hour)
print("coord:\(coord) - hour:\(hour)") // prints correct info
let datumContainer = try decoder.container(keyedBy: Datum.CodingKeys.self)
print("datumContainer keys:\(datumContainer.allKeys)") // shows []
let dateString = try datumContainer.decode(String.self, forKey: .dateString)
print("dateString:\(dateString)")
let currentTZ = TimeZone.currentOffsetFromUTCString
let isoDateString = "\(dateString)T\(hour)\(currentTZ)"
let isoDate = DateFormatter.Iso.date(from: isoDateString)
super.init(coord: coord, hour: hour, isoDate: isoDate)
}
}
在 AzimuthAtDate
class 中,我怎样才能访问 Datum.dateString
键以便我可以使用它的值来形成 isoDate
?
func decodeAzimuthAtDate(decoder: Decoder, key: Datum.CodingKeys) -> [AzimuthAtDate] {
var result = [AzimuthAtDate]()
do {
let con = try decoder.container(keyedBy: Datum.CodingKeys.self)
if let az = try con.decode([AzimuthAtDate]?.self, forKey: key)?.first {
result.append(az)
}
} catch let error {
print(error)
}
return result
}
忽略解码和JSON并从更一般的角度考虑问题。内部结构如何知道将其保存为 属性 的外部结构?
例如,这里会发生什么:
struct InnerStruct {
func test() {
// ?
}
}
struct OuterStruct {
let inner = InnerStruct()
let other = "howdy"
}
...为了调用 inner
的 test()
来打印 "howdy"
?显然 inner
的 InnerStruct 需要 reference 到持有它的 OuterStruct。像这样:
struct InnerStruct {
weak var container : OuterStruct?
func test() {
print(self.container?.other)
}
}
class OuterStruct {
var inner = InnerStruct()
let other = "howdy"
init() {
self.inner.container = self
}
}
let outerStruct = OuterStruct()
outerStruct.inner.test()
这就是您需要做的事情。您的内部类型需要一个 属性 这是对其容器的引用,以便它们可以“看到”该日期,并且您需要在配置其他所有内容时设置该引用。或者您甚至可以只使用外部日期来设置内部类型的 属性。但是你不能把它作为解码的一部分;必须在所有配置完成后完成。
根据您的数据(这是 playground 代码),这是一种可能性的简单示例:
let json = """
{
"11": [{
"name": "Sun",
"date": "2021-01-26",
"rising": [{
"coord": "113.6681",
"hour": "07:16"
}],
"transit-sup": [{
"coord": "32.4711",
"hour": "12:16"
}],
"setting": [{
"coord": "246.4743",
"hour": "17:17"
}]
}]
}
"""
let jsonData = json.data(using: .utf8)
class Inner: Decodable, CustomStringConvertible {
let coord : String
let hour : String
var outerDate : String?
var description : String {
return (self.coord + ";" + self.hour + ";" + (self.outerDate ?? ""))
}
}
struct Outer: Decodable {
let name: String
let date: String
let rising: [Inner]
let transitsup: [Inner]
let setting: [Inner]
enum CodingKeys: String, CodingKey {
case name
case date
case rising
case transitsup = "transit-sup"
case setting
}
}
struct Main: Decodable {
let x: [Outer]
enum CodingKeys: String, CodingKey {
case x = "11"
}
}
let result = try! JSONDecoder().decode(Main.self, from: jsonData!)
let outers = result.x
for anOuter in outers {
let date = anOuter.date
for anInner in anOuter.rising {
anInner.outerDate = date
}
for anInner in anOuter.setting {
anInner.outerDate = date
}
for anInner in anOuter.transitsup {
anInner.outerDate = date
}
}
print(outers)
如您所见,完成后,每个 Inner 都知道其容器的 date
。因此它可以继续计算日期时间。
我正在尝试使用来自 Datum
结构的已解析日期字符串 (dateString
) 在 AzimuthAtDate
class...
{
"11": [{
"name": "Sun",
"date": "2021-01-26",
"rising": [{
"coord": "113.6681",
"hour": "07:16"
}],
"transit-sup": [{
"coord": "32.4711",
"hour": "12:16"
}],
"setting": [{
"coord": "246.4743",
"hour": "17:17"
}]
}]
}
struct Datum: Codable, CustomStringConvertible {
let name: String
let dateString: String
let isoDate: Date?
let rising: [AzimuthAtDate]
let transit: [ElevationAtDate]
let setting: [AzimuthAtDate]
enum CodingKeys: String, CodingKey {
case name
case dateString = "date"
case isoDate
case rising = "rising"
case transit = "transit-sup"
case setting = "setting"
}
init(from decoder: Decoder) throws {
let con = try decoder.container(keyedBy: CodingKeys.self)
self.name = try con.decode(String.self, forKey: .name)
self.dateString = try con.decode(String.self, forKey: .dateString)
self.isoDate = try con.decode(Date.self, forKey: .dateString)
self.rising = decodeAzimuthAtDate(decoder: decoder, key: .rising)
self.transit = decodeElevationAtDate(decoder: decoder, key: .transit)
self.setting = decodeAzimuthAtDate(decoder: decoder, key: .setting)
}
}
class CoordHour: Codable {
let coord: String
let hour: String
var isoDate: Date?
init(coord: String, hour: String, isoDate: Date?) {
self.coord = coord
self.hour = hour
self.isoDate = isoDate
}
}
但是在尝试从 AzimuthAtDate
中访问 Datum.CodingKeys
的密钥时,datumContainer
没有密钥。所以我得到这样的错误:
keyNotFound(CodingKeys(stringValue: "date", intValue: nil) ... debugDescription: "No value associated with key CodingKeys(stringValue: \"date\", intValue: nil) (\"date\").", underlyingError: nil))
class AzimuthAtDate: CoordHour {
required init(from decoder: Decoder) throws {
let coordHourContainer = try decoder.container(keyedBy: CoordHour.CodingKeys.self)
let coord = try coordHourContainer.decode(String.self, forKey: .coord)
let hour = try coordHourContainer.decode(String.self, forKey: .hour)
print("coord:\(coord) - hour:\(hour)") // prints correct info
let datumContainer = try decoder.container(keyedBy: Datum.CodingKeys.self)
print("datumContainer keys:\(datumContainer.allKeys)") // shows []
let dateString = try datumContainer.decode(String.self, forKey: .dateString)
print("dateString:\(dateString)")
let currentTZ = TimeZone.currentOffsetFromUTCString
let isoDateString = "\(dateString)T\(hour)\(currentTZ)"
let isoDate = DateFormatter.Iso.date(from: isoDateString)
super.init(coord: coord, hour: hour, isoDate: isoDate)
}
}
在 AzimuthAtDate
class 中,我怎样才能访问 Datum.dateString
键以便我可以使用它的值来形成 isoDate
?
func decodeAzimuthAtDate(decoder: Decoder, key: Datum.CodingKeys) -> [AzimuthAtDate] {
var result = [AzimuthAtDate]()
do {
let con = try decoder.container(keyedBy: Datum.CodingKeys.self)
if let az = try con.decode([AzimuthAtDate]?.self, forKey: key)?.first {
result.append(az)
}
} catch let error {
print(error)
}
return result
}
忽略解码和JSON并从更一般的角度考虑问题。内部结构如何知道将其保存为 属性 的外部结构?
例如,这里会发生什么:
struct InnerStruct {
func test() {
// ?
}
}
struct OuterStruct {
let inner = InnerStruct()
let other = "howdy"
}
...为了调用 inner
的 test()
来打印 "howdy"
?显然 inner
的 InnerStruct 需要 reference 到持有它的 OuterStruct。像这样:
struct InnerStruct {
weak var container : OuterStruct?
func test() {
print(self.container?.other)
}
}
class OuterStruct {
var inner = InnerStruct()
let other = "howdy"
init() {
self.inner.container = self
}
}
let outerStruct = OuterStruct()
outerStruct.inner.test()
这就是您需要做的事情。您的内部类型需要一个 属性 这是对其容器的引用,以便它们可以“看到”该日期,并且您需要在配置其他所有内容时设置该引用。或者您甚至可以只使用外部日期来设置内部类型的 属性。但是你不能把它作为解码的一部分;必须在所有配置完成后完成。
根据您的数据(这是 playground 代码),这是一种可能性的简单示例:
let json = """
{
"11": [{
"name": "Sun",
"date": "2021-01-26",
"rising": [{
"coord": "113.6681",
"hour": "07:16"
}],
"transit-sup": [{
"coord": "32.4711",
"hour": "12:16"
}],
"setting": [{
"coord": "246.4743",
"hour": "17:17"
}]
}]
}
"""
let jsonData = json.data(using: .utf8)
class Inner: Decodable, CustomStringConvertible {
let coord : String
let hour : String
var outerDate : String?
var description : String {
return (self.coord + ";" + self.hour + ";" + (self.outerDate ?? ""))
}
}
struct Outer: Decodable {
let name: String
let date: String
let rising: [Inner]
let transitsup: [Inner]
let setting: [Inner]
enum CodingKeys: String, CodingKey {
case name
case date
case rising
case transitsup = "transit-sup"
case setting
}
}
struct Main: Decodable {
let x: [Outer]
enum CodingKeys: String, CodingKey {
case x = "11"
}
}
let result = try! JSONDecoder().decode(Main.self, from: jsonData!)
let outers = result.x
for anOuter in outers {
let date = anOuter.date
for anInner in anOuter.rising {
anInner.outerDate = date
}
for anInner in anOuter.setting {
anInner.outerDate = date
}
for anInner in anOuter.transitsup {
anInner.outerDate = date
}
}
print(outers)
如您所见,完成后,每个 Inner 都知道其容器的 date
。因此它可以继续计算日期时间。