Home > OS >  How can I decode a JSON array with multiple data types?
How can I decode a JSON array with multiple data types?

Time:11-28

I'm trying to decode a JSON file from an JSON API Call result enter image description here

CodePudding user response:

You can use QuickType to parse the model data from JSON.

// MARK: - DataModel
struct DataModel: Codable {
    let title: String
    let blanks: [String]
    let value: [Value]
}

enum Value: Codable {
    case integer(Int)
    case string(String)

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

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

Please use the following code to check the value type of Value below.

let jsonData = jsonString.data(using: .utf8)!
let dataModel = try? JSONDecoder().decode(DataModel.self, from: jsonData)
dataModel?.value.forEach { value in
    switch value {
    case .integer(let intValue):
        print(intValue)
    case .string(let stringValue):
        print(stringValue)
    }
}

CodePudding user response:

Decodable expects concrete types which conform to the protocol. Any(Object) is not supported.

You could decode Value as UnkeyedContainer manually. The result is the string array in strings and the integer value in integer.

struct DataModel: Decodable {
    let title: String
    let blanks: [String]
    let value: Value
}

struct Value: Decodable {
    let strings : [String]
    let integer : Int
    
    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        var stringData = [String]()
        guard let numberOfItems = container.count else {
            throw DecodingError.dataCorruptedError(in: container,
                                                   debugDescription: "Number of items in the array is unknown")
        }
        while container.currentIndex < numberOfItems - 1 {
            stringData.append(try container.decode(String.self))
        }
        strings = stringData
        integer = try container.decode(Int.self)
    }
}

Side note: A JSON value is never an object (reference type).

  • Related