0

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]}
6
  • The best solution is to make the changes on the server to send consistent data. Commented Dec 21, 2019 at 5:36
  • thank you @vadian for your reply but actually its not possible to change in the server side, is there any other solution ? Commented Dec 21, 2019 at 5:39
  • 3
    You could write a custom initializer which checks the types or declare the array as custom enum with associated types. In any case you have to customize the decoding process. Commented Dec 21, 2019 at 5:44
  • What are the specific kinds of "different data types." Do you mean it will be an array of "string or int or float?" Or are there other specific data types that can be in that that list? The examples you've given here aren't legal JSON, which makes answering the question difficult. Can you provide legal JSON and what kind of resulting struct you have in mind? In JSON, there's no distinction between ints and floats. They're all just "numbers." Do you intend there to be a distinction? How do you intend to distinguish between them? Can you give examples? Commented Dec 21, 2019 at 5:44
  • 1
    I wonder why there is no common type in standard library to unmarshal such common task Commented Dec 25, 2019 at 9:25

1 Answer 1

4

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)
}
Sign up to request clarification or add additional context in comments.

3 Comments

thank you @gereon, this code can handle the combination of string and double but the output is like string("pavan") double("5.5505") and I'm not able to convert it nor write a condition using this, can you please help me to solve this
Updated my answer. Find more on associated values here: docs.swift.org/swift-book/LanguageGuide/Enumerations.html#ID148
@PavanKumar you can also add utility functions like var stringValue: String? { ... } to a Datum if you don't want to do pattern matching all the time

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.