如何将递归数据映射到结构中?

时间:2020-06-11 03:39:54

标签: ios struct swiftui

此帖子与previous post I made有关。我希望映射以下嵌套字典:

["A": [["A1": ["A11", "A12"]], ["A2": ["A21", "A22"]]],
   "B": [["B1": ["B11", "B12"]], ["B2": ["B21", "B22"]]]
    ]

转换为递归结构:

Item(title:"",children:
    [Item(title:"A",children:
        [Item(title:"A1", children:
            [Item(title:"A11"),Item(title:"A12")]
            )]),
          Item(title:"B",children:
            [Item(title:"B1"),Item(title:"B2")]
        )]
)

struct Item: Identifiable {
    let id = UUID()
    var title: String
    var children: [Item] = []
}

为了进行实验,我从[“ A”:[[“ A1”:[“ A11”]]]]开始,并制作了一个json字符串:

let json1: String = """
            {"title": "", "children":[{"title": "A",
                                      "children": [{"title": "A1",
                                                    "children": [{"title": "A11"}]
                                                    }]
                                     }]
            }
"""



let decoder = JSONDecoder()
let info = try decoder.decode(Item.self, from: json.data(using: .utf8)!)
print(info)

仅当我在最后一个节点中包含“ children”:[]时,此方法才有效:

   let json2: String =  """
                {"title": "", "children":[{"title": "A",
                                          "children": [{"title": "A1",
                                                        "children": [{"title": "A11", "children": []}]
                                                    }]
                                     }]
            }
"""

我需要做些什么才能使json1字符串起作用,以便即使没有孩子的输入,它也将采用[]的默认值?

2 个答案:

答案 0 :(得分:0)

您必须提供自定义init(decoder:)来处理这种情况。处理此问题将需要您使用decodeIfPresent容器的JSONDecoder API,并尝试仅在存在值的情况下进行解码,并在条件失败时提供默认值。方法如下:

extension Item: Codable {
    enum CodingKeys: String, CodingKey {
        case title, children
    }
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        title = try container.decode(String.self, forKey: .title)
        children = try container.decodeIfPresent([Item].self, forKey: .children) ?? []
    }
}

答案 1 :(得分:0)

一种方法是使children为可选,并使用计算属性访问它。在childrennil的情况下,calculated属性可以返回一个空数组。

struct Item: Identifiable, Decodable {
    let id = UUID()
    var title: String
    private var privateChildren: [Item]?
    var children: [Item] {
        return self.privateChildren ?? []
    }

    enum CodingKeys: String,CodingKey {
        case title
        case privateChildren = "children"
    }
}

我使用了CodingKeys枚举,以便您可以在JSON和代码中保留相同的名称,但是如果您在JSON或代码中更改属性名称,则可以避免这种情况。