samedi 8 mai 2021

Why is the `Mirror.child.label` field not handling self-referential classes properly?

I am using reflection in Swift to write a generic object (Any) to dictionary in Swift; both for educational purposes and production purposes too.

The code works but one of my tests showed that for self-referential classes, it does not work properly.

When accessing the referenced member field whose type is same as the class, the Mirror.child.label field returns a value of some as the dictionary key , before accessing the member's variables.

A good example:

class Node{
  var next: Node!
  var val: Int = 0

init(_ val: Int){
self.val = val
}

}
  
 
let node = Node(4)
node.next = Node(5)
node.next.next = Node(6)


print("node.toDictionary: " , toDictionary(obj: node))

The output is:

node.toDictionary:  ["val": 4, "next": ["some": ["next": ["some": ["val": 6, "next": [:]]], "val": 5]]]

Needless to say, the expected output is:

["next": ["next": ["val": 6],"val": 5],"val": 4]

The minimal code for toDictionary which produces this error is:

func toDictionary(obj: Any) -> [String:Any] {
    var dict = [String:Any]()
    let otherObj = Mirror(reflecting: obj)
    for child in otherObj.children {
        if let key = child.label {
          
            if child.value is String || child.value is Character || child.value is Bool
                || child.value is Int  || child.value is Int8  || child.value is Int16  || child.value is Int32 || child.value is Int64
                || child.value is UInt  || child.value is UInt8  || child.value is UInt16  || child.value is UInt32 || child.value is UInt64
                || child.value is Float  || child.value is Float32 || child.value is Float64 || child.value is Double
            {
                dict[key] = child.value
            }
            else if child.value is [String] || child.value is [Character] || child.value is [Bool]
                || child.value is [Int]  || child.value is [Int8]  || child.value is [Int16]  || child.value is [Int32] || child.value is [Int64]
                || child.value is [UInt]  || child.value is [UInt8]  || child.value is [UInt16]  || child.value is [UInt32] || child.value is [UInt64]
                || child.value is [Float]  || child.value is [Float32] || child.value is [Float64] || child.value is [Double]{
                
                let array = child.value as! [Any]
                
                
                dict[key] = array
                
                
            }else if child.value is [Any]{
                 let array = child.value as! [Any]
                 var arr:[Any] = []
                for any in array{
                    arr.append(toDictionary(obj: any))
                }
                dict[key] = arr
            }
            else{
                dict[key] = toDictionary(obj: child.value)
            }
            
        }
    }
    return dict
}

Why is the Mirror.child.label field not handling self-referential classes properly?





Aucun commentaire:

Enregistrer un commentaire