Home > Net >  Decoding JSON attribute of ambiguous type in iOS Swift
Decoding JSON attribute of ambiguous type in iOS Swift

Time:08-25

How would you write the decoding code for the following JSON:

{
   "identifier": "1",
   "issuer": "visa",
   "pattern": [5, [7, 9]]

}

To map it into the following model:

struct Card: Decodable {
    let identifier: String
    let issuer: String
    let pattern: [CardPattern]
}

enum CardPattern: Decodable {
    case prefix(Int)
    case range(start: Int, end: Int)
}

Notice how the pattern attribute in the json is a collection of two possible values:

  • Int indicating we should map into a CardPattern.prefix case
  • [Int] containing two values, indicating we should map into a CardPattern.range case (first value is start, second value is end)

CodePudding user response:

Read about the singleValueContainer it should solve your problem. Here is a working snippet with testing...

enum CardPattern: Codable {
    case prefix(Int)
    case range(start: Int, end: Int)
    
    init(from decoder: Decoder) throws {
        let singleContainer = try decoder.singleValueContainer()
        do {
            let prefix = try singleContainer.decode(Int.self)
            self = .prefix(prefix)
            return
        } catch {
            print("Not a prefix")
        }
        
        do {
            let range = try singleContainer.decode([Int].self)
            self = .range(start: range[0], end: range[1])
            return
        } catch {
            print("Not a range")
        }
        throw NSError(domain: "Unknown type", code: -1)
    }
    
    func encode(to encoder: Encoder) throws {
        var singleContainer = encoder.singleValueContainer()
        switch self {
        case .prefix(let value):
            try singleContainer.encode(value)
        case .range(start: let start, end: let end):
            let range: [Int] = [start, end]
            try singleContainer.encode(range)
        }
    }
    
}


let data = [CardPattern.prefix(10), CardPattern.range(start: 2, end: 9)]
var encodedData: Data = Data()
do {
    encodedData = try JSONEncoder().encode(data)
    print(encodedData)
} catch {
    print("encode")
    print(error)
}
do {
    let decoded = try JSONDecoder().decode([CardPattern].self, from: encodedData)
    print(decoded)
} catch {
    print("decode")
    print(error)
}

  • Related