Search code examples
jsonswiftjson-rpcjsondecoder

How to Parse Json dictonary in swift 4


Hi I have a problem with this Json:

{
    "id": "libMovies",
    "jsonrpc": "2.0",
    "result": {
        "limits": {
            "end": 75,
            "start": 0,
            "total": 1228
        },
        "movies": [{
            "art": {
                "fanart": "myfanart",
                "poster": "myposter"
            },
            "file": "myfile",
            "label": "mylable",
            "movieid": mymovieid,
            "playcount": 0,
            "rating": myrating,
            "thumbnail": "mythumbnail"
        }]
    }
}

When I parse Json in swift 5 with this code

try! JSONDecoder().decode([MyMovie].self, from: data!)

I get this error

Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil)):

How can I solve this?


Solution

  • For the below JSON,

    {"id":"libMovies","jsonrpc":"2.0","result":{"limits":{"end":75,"start":0,"total":1228},"movies":[{"art":{"fanart":"myfanart","poster":"myposter"},"file":"myfile","label":"mylable","movieid":"mymovieid","playcount":0,"rating":"myrating","thumbnail":"mythumbnail"}]}}
    

    The Codable models that you need to use,

    struct Root: Decodable {
        let id, jsonrpc: String
        let result: Result
    }
    struct Result: Decodable {
        let limits: Limits
        let movies: [Movie]
    }
    
    struct Limits: Decodable {
        let end, start, total: Int
    }
    
    struct Movie: Decodable {
        let art: Art
        let file, label, movieid: String
        let playcount: Int
        let rating, thumbnail: String
    }
    struct Art: Decodable {
        let fanart, poster: String
    }
    

    Parse the JSON data like so,

    do {
        let response = try JSONDecoder().decode(Root.self, from: data)
        print(response.result.movies.map({"file: \($0.file), label: \($0.label)"}))
    } catch {
        print(error)
    }
    

    Edit:

    To save the movies separately, create a variable of type [Movie],

    var movies = [Movie]()
    

    Now, while parsing save the response.result.movies in the above created property,

    do {
        let response = try JSONDecoder().decode(Root.self, from: data)
        print(response.result.movies.map({"file: \($0.file), label: \($0.label)"}))
        movies = response.result.movies
    } catch {
        print(error)
    }