【问题标题】:How to map recursive data into structure?如何将递归数据映射到结构中?
【发布时间】:2020-09-30 15:24:00
【问题描述】:

这篇文章与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字符串工作,这样即使没有children的输入,它也会采用[]的默认值?

【问题讨论】:

    标签: ios struct swiftui


    【解决方案1】:

    您必须提供自定义init(decoder:) 来处理这种情况。处理此问题需要您使用JSONDecoder 的容器的decodeIfPresent 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) ?? []
        }
    }
    

    【讨论】:

    • 我刚刚尝试了您的解决方案,谢谢,它有效。我是 swift 新手,只想知道 init(decoder: ) 在哪一行被调用?是在“let info = try decoder.decode(Item.self, from: json.data(using: .utf8)!)”中吗?怎么叫
    • 是的,当您尝试解码时。编译器调用该方法进行解码。
    【解决方案2】:

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

    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 或代码中更改了属性名称,则可以避免这种情况。

    【讨论】:

    • 我猜这里有些东西不起作用,它显示:Item(id: DEB3941C-F13C-425A-A34E-C1382B545E12, title: "", privateChildren: Optional([__lldb_expr_171.Item(id: D83563AA -5AF9-4B52-8480-9742178DC3BD,标题:“A”,privateChildren:可选([__lldb_expr_171.Item(id:B8163623-0A70-4C01-BA15-1148A42BA6FF,标题:“A1”,privateChildren:可选([__lldb_expr_171.Item (id: 05D09104-50BC-4050-AE43-5BF1754B2A29, title: "A11", privateChildren: nil)]))]))]))
    • 我希望以“children”为key,最后一个privateChildren为nil但不是[]
    • children 是一个计算属性,所以如果你只是print(item),它不会显示,但它会有你想要的值。试试print(item.children)
    • 啊,明白了。对不起,这是一个严重的错误。最后一个孩子是 []: print(info.children[0].children[0].children[0].children) 显示 [].
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-07
    • 1970-01-01
    • 2017-06-29
    • 2017-05-15
    • 2012-02-06
    • 2010-09-05
    相关资源
    最近更新 更多