Home > front end >  Swift Decodable JSON Handle data
Swift Decodable JSON Handle data

Time:10-31

I'm trying to decode a JSON with swift codable but I'm having issues on how to handle the data, especially because the structure is dynamic, so I've used an enum to parse the JSON.

After parsing the data I'm trying to use an array map but gives me errors

Here is a JSON sample (if you need the full version here the URL) :

{
  "kind": "Listing",
  "data": {
    "after": null,
    "dist": 1,
    "modhash": "",
    "geo_filter": "",
    "children": [
      {
        "kind": "t1",
        "data": {
          "title": "Test",
          "score": 88,
          "replies": {
            "kind": "Listing",
            "data": {
              "after": null,
              "dist": null,
              "modhash": "",
              "geo_filter": "",
              "children": [
                {
                  "kind": "t1",
                  "data": {
                    "title": "Test",
                    "score": 88
                  }
                },
                {
                  "kind": "t1",
                  "data": {
                    "title": "post",
                    "score": 12
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

Here is how I have parsed the JSON

import Foundation

enum DataType : String, Decodable {
    
    case listing = "Listing"

    case t1

    case t3

    case more
}

indirect enum Parse : Decodable {
    
    case listing(Listing)
    case t1(Comment)
    case t3(Comment)
    case more(More)

    enum CodingKeys : String, CodingKey {
        
        case kind
        case data
        
    }
    
    init(from decoder: Decoder) throws {
        
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let kind = try container.decode(DataType.self, forKey: .kind)
        
        switch kind {
            
            case .listing:
            self = .listing(try container.decode(Listing.self, forKey: .data))
            case .t1:
                self = .t1(try container.decode(Comment.self, forKey: .data))
            case .t3:
                self = .t3(try container.decode(Comment.self, forKey: .data))
            case .more:
                self = .more(try container.decode(More.self, forKey: .data))
        
        }
    }
}

I'm using a model so model.listings should contain the result data parsed in the code below

let decoder = JSONDecoder()
var result = try decoder.decode([Parse].self, from: data!)
self.listings = result 

In the terminal, if I PO model.listings the data is present

Here is an example

 2 elements
   0 : Parse
     listing : Listing
       paginator : Paginator
         after : Optional<String>
          - some : ""
         before : Optional<String>
          - some : ""
         modhash : Optional<String>
          - some : ""
       children : 1 element
         0 : Parse
           t3 : Comment
             id : 51C7640E-D382-467B-9A65-6CAB04FA470E
              - uuid : "51C7640E-D382-467B-9A65-6CAB04FA470E"
             title : Optional<String>
              - some : "test"
             score : Optional<Int>
              - some : 88
            - replies : nil

My issue is that I don't know how to handle that data

I've tried with something like:

var test = model.listings.map( { $0.listing } )

But it says "Enum case 'listing' cannot be used as an instance member"

Also tried this way:

var test = model.listings.listing[0].children

In this case the error is: Value of type '[Parse]' has no member 'listing'

I just started with swift so I'm really confused right now, what's the best way to handle that kind of data?

CodePudding user response:

Try using one of these mechanisms:

// Using a Switch to enumerate the possibilities and using a pattern to extract the associated data
switch result {
    case .listing(let aListing) :
        print(aListing.geo_filter)
    case .t1(let comment) :
        print(comment.title)
    case .t3(let comment) :
        print(comment.title)
}


// Using a if case with a pattern to extract one particular enum case
if case .listing(let aListing) = result  {
    debugPrint(aListing)
}
  • Related