Godfather
Godfather

Reputation: 4330

Swift: Decodable

Lets say i have this json from an API request:

friends: {  
   "john":31,
   "mark":27,
   "lisa":17,
   "tom":41
}

I usually expect it in an array format:

friends: [  
   { "john":31 },
   { "mark":27 },
   { "lisa":17 },
   { "tom":41 }
]

But the API doesn't provide me this way the results. So i want finally to map it to an array of [Friend], where Friend is:

class Friend: Decodable {
  let name: String
  let age: Int
}

How should i serialize this json to get [Friend] ?

Upvotes: 2

Views: 194

Answers (2)

user28434'mstep
user28434'mstep

Reputation: 6600

Disclaimer: I recommend to change you API format to one like in @scriptable's answer (which was deleted while I was answering, hm), where name and age fields are properly defined. And where you're not limited to basically a pair of key-value to parse.

But if you can't or won't change it you may use something like this to decode your Friend type:

struct Friend: Decodable {
    let name: String
    let age: UInt

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let dictionary = try container.decode([String: UInt].self)

        guard dictionary.count == 1, let (name, age) = dictionary.first else {
            throw DecodingError.invalidFriendDictionary
        }

        self.name = name
        self.age = age
    }

    enum DecodingError: Error {
        case invalidFriendDictionary
    }
}

struct Friends: Decodable {
   let friends: [Friend]
}

let friends = try JSONDecoder().decode(Friends.self, from: data)

It assumes key is name and value is age. And checks that there's only a one pair to parse.

Upvotes: 0

Maxim Kosov
Maxim Kosov

Reputation: 1980

First of all, example isn't valid json at all. To be valid it either shouldn't include "friends" label, or it should be embedded in another object like this

{
  "friends": {  
    "john":31,
    "mark":27,
    "lisa":17,
    "tom":41
  }
}

If I understand question correctly, you want to decode json object to swift array. I don't think there is a way to do so without writing custom decoding. Instead, you can decode json into Dictionary and when manually map it like so

struct Friend {
    let name: String
    let age: Int
}

struct Friends: Decodable {
    let friends: [String: Int]
}

let friends = try! JSONDecoder().decode(Friends.self, from: json.data(using: .utf8)!)
    .friends
    .map { (name, age) in Friend(name: name, age: age) }

Upvotes: 4

Related Questions