繁体   English   中英

如何获取嵌套结构中的所有属性名称

[英]How to get all property names in nested Structs

让我们假设我有以下结构:

struct Location: Codable, Loopable {
    var altitude: Double?
    var coordinate: Coordinate?
    struct Coordinate: Codable, Loopable {
        var latitude: Double?
        var longitude: Double?
    }
    var course: Double?
    var courseAccuracy: Double?
    var floor: Floor?
    struct Floor: Codable, Loopable {
        var level: Int?
    }
    var horizontalAccuracy: Double?
    var speed: Double?
    var speedAccuracy: Double?
    var timestamp: Timestamp?
    struct Timestamp: Codable, Loopable {
        var year: Int?
        var month: Int?
        var day: Int?
        var hour: Int?
        var minute: Int?
        var second: Int?
    }
    var verticalAccuracy: Double?
    var deviceName: String?
    var validPosition: Bool?
}

现在我想为结构体实现两种方法。 一个应该返回名称中包含所有父项的所有属性名称,另一个应该返回这些属性的所有值。

第一个方法的结果应该是这样的(让我们将其命名为 allProperties()):

["altitude", "coordinate.latitude", "coordinate.longitude", "course", "courseAccuracy", "floor.level", "horzontalAccuracy", "speed", "speedAccuracy", "timeststamp.year", "timestamp.month", "timestamp.day", "timeststamp.hour", "timestamp.minute", "timestamp.second", "verticalAccuracy", "deviceName", "validPosition"]

第二种方法的结果(让我们将其命名为 allValues())应该如下所示:

[500.0, 48.000000, 10.00000, 120.0, 5.0, 4, 5.0, 3.0, 1.0, 2021, 07, 25, 22, 43, 50, 10.0, "iPhone", true]

正如您所看到的,我的示例结构与其他结构嵌套在一起。 在我的实际项目中,结构有两个以上的嵌套“级别”。 此外,属性类型也不是唯一的。 在最底层,它们都是基本数据类型(Double、Bool、String、Int)。

我尝试使用来自该线程的递归方法修改解析嵌套结构的解决方案,这似乎是一个优雅的解决方案: How to loop over struct properties in Swift?

现在我的问题:

  • 属性名称的方法只给出一个带有“some”的数组。
  • 但是属性值的方法返回正确的数组

["some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some"]

到目前为止,这是我的协议代码:

protocol Loopable {
    func allProperties(limit: Int) -> [String]
    func allValues(limit: Int) -> [Any]
}

extension Loopable {
    func allProperties(limit: Int = Int.max) -> [String] {
        return props(obj: self, count: 0, limit: limit)
    }
    
    func allValues(limit: Int = Int.max) -> [Any] {
        return values(obj: self, count: 0, limit: limit)
    }

    private func props(obj: Any, count: Int, limit: Int) -> [String] {
        let mirror = Mirror(reflecting: obj)
        var result: [String] = []
        for (prop, val) in mirror.children {
            guard let prop = prop else { continue }
            if limit == count {
                result.append(prop)
            } else {
                let subResult = props(obj: val, count: count + 1, limit: limit)
                subResult.count == 0 ? result.append(prop) : result.append(contentsOf: subResult)
            }
        }
        return result
    }
    
    private func values(obj: Any, count: Int, limit: Int) -> [Any] {
        let mirror = Mirror(reflecting: obj)
        var result: [Any] = []
        for (_, val) in mirror.children {
            //guard let val = val else { continue }   // This line does not compile
            if limit == count {
                result.append(val)
            } else {
                let subResult = values(obj: val, count: count + 1, limit: limit)
                subResult.count == 0 ? result.append(val) : result.append(contentsOf: subResult)
            }
        }
        return result
    }
}

我在这里做错了什么? 为什么属性标签总是“一些”?

所以“some”的问题源于你的值是可选的,可选值是一个枚举,所以你的反射正在选择它。 这就是为什么您不能在private func values(obj: Any, count: Int, limit: Int) -> [Any]因为您没有展开到具体类型。

当您的所有子类型都确认Loopable我们可以重构您的代码以检查类型是否符合Loopable 检查某物是否为空也比它的计数为零更有效,因此您应该尽可能使用该属性。

我们现在使用prop值作为前缀,以便我们可以获得您正在寻找的名称,但是,因为您的值是可选的,它们被包装在一个枚举中,所以您必须从字符串中.some val的类型是Any ,这意味着如果它是可选的,我们无法在不知道其具体类型的情况下解开它,因此我们需要执行上述操作以从前缀中删除.some


import Foundation


protocol Loopable {
    func allProperties() -> [String]
}

extension Loopable {
    func allProperties() -> [String] {
        return props(obj: self)
    }
    
    private func props(obj: Any, prefix: String = "") -> [String] {
        let mirror = Mirror(reflecting: obj)
        var result: [String] = []
        for (prop, val) in mirror.children {
            guard var prop = prop else { continue }
   
            // handle the prefix
            if !prefix.isEmpty {
                prop = prefix + prop
                prop = prop.replacingOccurrences(of: ".some", with: "")
            }
   
            if let _ = val as? Loopable {
                let subResult = props(obj: val, prefix: "\(prop).")
                subResult.isEmpty ? result.append(prop) : result.append(contentsOf: subResult)
            } else {
                result.append(prop)
            }
        }
        return result
    }
}
                    
           

这是一个简单的结构,我们可以用它来测试上面的代码。

struct User: Loopable {
    let name: String
    let age: Age?
    
    struct Age: Loopable {
        let value: Int?
        let day: Day?
        
        struct Day: Loopable {
            let weekday: Int?
        }
    }
}

let user = User(name: "mike", age: .init(value: 20, day: .init(weekday: 5)))

print(user.allProperties())

这将打印出以下内容

["name", "age.value", "age.day.weekday"]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM