【问题标题】:How to nest type casting in swift?如何快速嵌套类型转换?
【发布时间】:2020-04-04 17:08:57
【问题描述】:

我有一个包含嵌套字典的 json 对象,我正在尝试将其解析为 swift:

{
    "ambientLightColor" : [100.0, 200.0, 300.0],

    "lights" : {
        "light1" : {"position" : [ 5, 5, 0], "direction" : [0,0,0], "color" : [0.0,0.7,0.3]},
        "light2" : {"position" : [-5, 5, 0], "direction" : [0,0,0], "color" : [0.8,0.2,0.1]},
        "light3" : {"position" : [ 0,-5, 0], "direction" : [0,0,0], "color" : [0.3,0.3,0.9]}
    }  
}

由于根对象是一个字典,其键为字符串,因此我将其转换为开头,如https://developer.apple.com/swift/blog/?id=37 示例中所做的那样

 if let scene = sceneJSON as? [String: Any] {

    if let ambientRGB = scene["ambientLightColor"] as? [f1] {
        ambientLightColor = f3(ambientRGB[0],ambientRGB[1],ambientRGB[2])
    }

    if let lights = scene["lights"] as? [String: Any] {
        for (lightName, properties) in lights {
            let position = properties["position"]
            let direction = properties["direction"]
            let color = properties["color"]
            self.lights.append(Light(position: f3(position[0], position[1], position[2]), direction: f3(direction[0], direction[1], direction[2]), color: f3(color[0], color[1], color[2])))
        }
    }

}

由于根字典值首先被转换为“任何”,因此在使用条目时必须将它们转换为更可用的类型。这对于我使用 if let ... as? 的第一个条目来说已经足够简单了。 [f1] 在使用它们创建 float3 之前将字典值 [100.0, 200.0, 300.0] 转换为单个浮点数组(我不确定如何从浮点数组直接转换为 float3 但这并不重要)。

第二种情况有点困难,因为它再次包含另一个嵌套字典,尽管我不想为每个单独的元素使用 if let,而是我想使用如上所示的 for 循环。现在我知道我还必须将属性字典值从 Any 类型转换为形式为 [String: [f1]] 的字典。

我已经尝试在第一个字典转换中转换它:

    if let lights = scene["lights"] as? [String: [String: [f1]] {
        for (lightName, properties) in lights {
            let position = properties["position"]
            let direction = properties["direction"]
            let color = properties["color"]
            self.lights.append(Light(position: f3(position[0], position[1], position[2]), direction: f3(direction[0], direction[1], direction[2]), color: f3(color[0], color[1], color[2])))
        }

这给了我一个 [f1] 是可选的错误,并且当我运行它时也找不到任何匹配此类型的 json 对象。

我也试过

    if let lights = scene["lights"] as? [String: [String: Any] {
        for (lightName, properties) in lights {
            let position = properties["position"] as! [f1]
            let direction = properties["direction"] as! [f1]
            let color = properties["color"] as! [f1]
            self.lights.append(Light(position: f3(position[0], position[1], position[2]), direction: f3(direction[0], direction[1], direction[2]), color: f3(color[0], color[1], color[2])))
        }

虽然这给了我任何关于无法将 NSNumber 桥接到 float 的错误,但当它们在数组中时很难修复。

我也试过

    if let lights = scene["lights"] as? [String: Any] {
        for case let (lightName, properties as? [String:[f1]]) in lights {
            let position = properties["position"]
            let direction = properties["direction"]
            let color = properties["color"]
            self.lights.append(Light(position: f3(position[0], position[1], position[2]), direction: f3(direction[0], direction[1], direction[2]), color: f3(color[0], color[1], color[2])))
        }

这似乎是编写解决方案的好方法,但 swift 不会接受字典对括号内的类型转换。

非常感谢一个简洁的方式来做到这一点。 谢谢。

【问题讨论】:

  • f1f3 是什么?它们在哪里定义?
  • 考虑使用Codable 或至少像SwiftyJSON 这样的库。手动选项真的很痛苦。
  • @Alexander-ReinstateMonica 道歉,它们是 Float 和 SIMD3 的类型别名
  • @Yonat Codable 似乎是一个更好的解决方案,尽管我认为没有任何方法可以解释对象中存在的不同参数,因为结构具有明确定义的属性,而不是能够选择只有在场的人。我快速浏览了 SwiftyJSON,我认为它可能是最合适的。谢谢!
  • " 以任何方式解释对象中存在的不同参数,因为结构具有明确定义的属性,而不是只能从存在的属性中进行选择" 你能解释一下你的意思吗?

标签: ios swift dictionary types casting


【解决方案1】:

那篇博文已有 4 年历史。自从引入 Codable 之后,Swift 中的 JSON 解析就完全不同了。

假设ff3 分别是FloatSIMD3<Float> 的类型别名,那么您可以只使用:

import Foundation
import simd
let json = """
{
    "ambientLightColor" : [100.0, 200.0, 300.0],

    "lights" : {
        "light1" : {"position" : [ 5, 5, 0], "direction" : [0,0,0], "color" : [0.0,0.7,0.3]},
        "light2" : {"position" : [-5, 5, 0], "direction" : [0,0,0], "color" : [0.8,0.2,0.1]},
        "light3" : {"position" : [ 0,-5, 0], "direction" : [0,0,0], "color" : [0.3,0.3,0.9]}
    }  
}
"""

struct Light: Codable {
    let position: SIMD3<Float>
    let direction: SIMD3<Float>
    let color: SIMD3<Float>
}

struct Scene: Codable {
    let ambientLightColor: SIMD3<Float>
    let lights: [String: Light]
}

let jsonData = json.data(using: .utf8)!
let scene = try! JSONDecoder().decode(Scene.self, from: jsonData)
print(scene)

【讨论】:

  • 是的,这似乎是一个不错的选择,我确实希望有一种方法可以只解码某些属性,但似乎可选选项允许这样做。谢谢
【解决方案2】:

我相信您遇到的问题可以通过迭代键来规避,而不是使用 for (key, value) 风格。这里值得深思。此外,对于 NSNumber/Float 问题,我发现转换为 [Double] 并映射到 [Float] 效果很好,但转换为 [Float] 失败。

for key in lights.keys {
    let lightDict = lights[key] as! [String: Any]
    let position = lightDict["position"] as! [Double]
    let direction = lightDict["direction"] as! [Double]
    let color = lightDict["color"] as! [Double]

    print(position)
    print(direction)
    print(color)

    let floats = color.map { Float($0) }
    print(floats)
}

【讨论】:

  • 谢谢!主要问题确实是从 NSNumber 转换为 Float。迭代器还引起了一些奇怪的问题,这些问题与使用需要更多上下文的 Float 初始化器有关,所以这也解决了这个问题。但是,我确实喜欢代码的清晰和简洁,所以我可能会探索其他选项,除非开发人员改进了序列化。干杯
猜你喜欢
  • 2010-12-25
  • 1970-01-01
  • 1970-01-01
  • 2016-01-22
  • 1970-01-01
  • 2015-09-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多