Pavan Kumar
Pavan Kumar

Reputation: 162

Decode json array data with different datatypes in IOS using struct decodable swift 4/5

I've tried to construct struct and decode using it, but it only works if all the datatype is same as defined

For example the below code works fine:

{"key1": "stringValue", "key2": intValue, "key3": ["stringData1", "stringData2", "stringData3"]}
struct User: Decodable               
{
    var key1: String
    var key2: Int
    var key3: [String]
}

let decoder = JSONDecoder()
let decodedJsonData = try decoder.decode(User.self, from: data)
print(decodedJsonData)

What should I do to decode if key3 contains different data types?

{"key1": "stringValue", "key2": intValue, "key3": ["stringData1", IntData, FloatData]}

Upvotes: 0

Views: 1291

Answers (1)

Gereon
Gereon

Reputation: 17882

Use enums with associated values:

struct User: Codable {
    let command, updated: Int
    let data: [Datum]
}

enum Datum: Codable {
    case double(Double)
    case string(String)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(Double.self) {
            self = .double(x)
            return
        }
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        throw DecodingError.typeMismatch(Datum.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Datum"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .double(let x):
            try container.encode(x)
        case .string(let x):
            try container.encode(x)
        }
    }
}

to get at the individual values in data, use code like this:

let json = """
    {"command": 1, "updated": 2, "data": ["stringData1", 42, 43]}
    """.data(using: .utf8)

do {
    let user = try JSONDecoder().decode(User.self, from: json!)

    for d in user.data {
        switch d {
        case .string(let str): print("String value: \(str)")
        case .double(let dbl): print("Double value: \(dbl)")
        }
    }
} catch {
    print(error)
}

Upvotes: 4

Related Questions