0

I'm completely new on SwiftUI and trying to load a json file with data containing a tree structure with items of this type:

struct Node : Identifiable {
 id = UUID()
 name : String
 childNodes : [Node]
}

It is unknown beforehand how deep the data structure goes with child nodes.

Have tried to just decode

guard let results = try? decoder.decode(Node.self, from: filedata) else {
            fatalError("Decode error")

which seems to work, until I try to use the data. Guess it might have to do with the fact that the childNodes array is empty at some level.

What would be the best way to accomplish this?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • 2
    What does not work? Replace `try?` with `try` and a `do - catch` block and print the `error`. And your struct does not conform to `Decodable` anyway – vadian Feb 19 '21 at 17:51
  • 1
    You'll need to show the JSON. I expect that at some point you're missing the `childNodes` element, which your struct requires. If it can be missing (rather than empty), then it needs to be an Optional `childNodes: [Node]?`, or you need to use a custom init to decode this (which is generally my preferred solution over optional arrays). But I doubt the problem is the nesting itself. (Agreed with vadian that you shouldn't use `try?` if you can help it here. It just throws away the error that you need to debug this.) – Rob Napier Feb 19 '21 at 18:21
  • Yes, I think a custom init would be the best way, but not sure how to do that. The sample JSON I use to try to load is just like this: { "name" : "Root", "id" : 0, "nodes" : [ { "name" : "First child", "id" : 1, "nodes" : [] }] } – JonasEEriksson Feb 19 '21 at 22:17
  • One potential issue is that in Swift you have childNodes in your Node struct. This looks like it should correspond to "nodes" in JSON. This could lead to problems with decoding as they are not exactly the same name. As mentioned above, you should try to conform to the "Codable" Protocol. This will encapsulate encodable and decodable. I recommend further reading on this. Doing this will also allow you to have custom coding keys by declaring ```private enum CodingKeys: String, CodingKey { case childNodes = "nodes" } ``` – cjpais Feb 19 '21 at 22:49
  • Made it work with a custom init and "if-else" with decodeIfPresent – JonasEEriksson Feb 20 '21 at 00:01

2 Answers2

0

Not sure if this will help, but I solved a vaguely similar problem by using old-fashioned JSONSerialization. By casting to [String : Any] you don't need to know the structure of the nested objects. It is less clean to dig into them, but that's the trade-off:

guard let results = try JSONSerialization
    .jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
    print("didn't work")
    return 
}
John Nimis
  • 586
  • 3
  • 15
0

If the key nodes exists in any dictionary and an empty array is represented by [] then the JSON can be decoded simply with this struct

struct Node : Decodable {
    let id : Int
    let name : String
    let nodes : [Node]
}

For convenience you can add a computed property to indicate that the node is a leaf

struct Node : Decodable {
    let id : Int
    let name : String
    let nodes : [Node]
    
    private enum CodingKeys : String, CodingKey { case id, name, nodes }

    var isLeaf : Bool { return nodes.isEmpty }
}
 
vadian
  • 274,689
  • 30
  • 353
  • 361