我试图创建一个小应用程序来用SwiftUI控制我的Hue灯光,但我就是不能设法解码/迭代这个JSON。有这么多线程如何做到这一点,我已经尝试了几十个,使用CodingKeys,创建自定义解码器,等等,但我似乎不能得到它。所以这是我从我的Hue Bridge得到的JSON:
{
"1": {
"state": {
"on": true,
"bri": 254,
"hue": 8417,
"sat": 140,
"effect": "none",
"xy": [
0.4573,
0.41
],
"ct": 366,
"alert": "select",
"colormode": "ct",
"mode": "homeautomation",
"reachable": false
},
"swupdate": {
"state": "noupdates",
"lastinstall": "2021-08-26T12:56:12"
},
...
},
"2": {
"state": {
"on": false,
"bri": 137,
"hue": 36334,
"sat": 203,
"effect": "none",
"xy": [
0.2055,
0.3748
],
"ct": 500,
"alert": "select",
"colormode": "xy",
"mode": "homeautomation",
"reachable": true
},
"swupdate": {
"state": "noupdates",
"lastinstall": "2021-08-13T12:29:48"
},
...
},
"9": {
"state": {
"on": false,
"bri": 254,
"hue": 16459,
"sat": 216,
"effect": "none",
"xy": [
0.4907,
0.4673
],
"alert": "none",
"colormode": "xy",
"mode": "homeautomation",
"reachable": true
},
"swupdate": {
"state": "noupdates",
"lastinstall": "2021-08-12T12:51:18"
},
...
},
...
}
所以结构基本上是一个动态的关键字在根级别和轻的信息,这是相同的.我已经创建了以下类型:
struct LightsObject: Decodable {
public let lights: [String:LightInfo]
}
struct LightInfo: Decodable {
var state: StateInfo
struct StateInfo: Decodable {
var on: Bool
var bri: Int
var hue: Int
var sat: Int
var effect: String
var xy: [Double]
var ct: Int
var alert: String
var colormode: String
var reachable: Bool
}
var swupdate: UpdateInfo
struct UpdateInfo: Decodable {
var state: String
var lastinstall: Date
}
...
(it基本上继续包含对象中的所有变量)
我现在的问题是,我似乎无法将其放入普通的数组或字典中。我会满足于像{“1”:LightInfo,“2”,LightInfo}这样的东西,在那里我可以迭代,或者只是一个简单的[LightInfo,LightInfo,...],因为我甚至可能不需要索引。
然后,理想情况下,我可以做一些像
ForEach(lights) { light in
Text(light.name)
}
我试过创建一个自定义的编码键,实现类型为Codable,等等,但我找不到一个解决方案,适合我.我知道,有很多关于这个主题的线程,但我觉得我的初始设置可能是错误的,这就是为什么它不工作.
这是解码部分:
let task = urlSession.dataTask(with: apiGetLightsUrl, completionHandler: { (data, response, error) in
guard let data = data, error == nil else { return }
do {
let lights = try JSONDecoder().decode([String: LightInfo].self, from: data)
completionHandler(lights, nil)
} catch {
print("couldn't get lights")
completionHandler(nil, error)
}
})
我正在实际上得到JSON,没有问题,但我还没有能够解码它,正如我所说的。最新的错误是:Optional(Swift.DecodingError.typeMismatch(Swift.Double, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "1", intValue: nil), CodingKeys(stringValue: "swupdate", intValue: nil), CodingKeys(stringValue: "lastinstall", intValue: nil)], debugDescription: "Expected to decode Double but found a string/data instead.", underlyingError: nil)))
我看过一些帖子,但是他们使用的JSON通常有一个顶级对象,比如
{
"foo":
{
"bar": "foobar"
...
},
{
"bar": "foobar2"
...
},
{
"bar": "foobar3"
...
}
...
}
所以对它的处理有点不同,因为我可以创建一个结构体,如
struct Object: Decodable {
var foo: [LightsInfo]
}
这是不可能的:(
有人能给我指个方向吗?谢谢!
更新:
谢谢大家,解决方案确实有效。我在我的结构中有一些错误,现在已经修复了。(拼错的变量名,可选值等)。现在唯一缺少的是如何通过字典循环。如果我使用
ForEach(lights)...
Swift抱怨说,它只应该用于静态字典。我试过这样做
ForEach(lights, id: \.key) { key, value in
Button(action: {print(value.name)}) {
Text("foo")
}
}
但我得到了这个错误:Generic struct 'ForEach' requires that '[String : LightInfo]' conform to 'RandomAccessCollection'
更新二:
这似乎起作用了:
struct LightsView: View {
@Binding var lights: [String:LightInfo]
var body: some View {
VStack {
ForEach(Array(lights.keys.enumerated()), id: \.element) { key, value in
LightView(lightInfo: self.lights["\(value)"]!, lightId: Int(value) ?? 0)
}
}
}
}
我将尝试清理代码并优化它一点。)
4条答案
按热度按时间rpppsulh1#
你看起来差不多了。这是一个
[String:LightInfo]
。你只需要解码它(而不是将其 Package 在JSON中不存在的LightsObject中)。如果你不关心数字,你可以提取值。它应该是这样的:j8ag8udp2#
我自己也遇到了类似的问题,我想处理给定的泛型键。这是我解决它的方法,适应你的代码。
wd2eg0qa3#
如果您只对根字典的值感兴趣,请添加自定义初始化器
并解码
它返回
LightInfo
对象的数组,您可以声明当然,完成处理程序类型必须是
([LightInfo]?, Error?) -> Void
。pw9qyyiw4#
下面是这个Codable模型的一个示例用法: