0

I have an json array with a list of item, that has different property.

{
    "items": [ 
        {
            "id": "1",
            "name": "name",
            "propertyOfA": "1243",
            "productype": "a"

        },
        {
            "id": "2",
            "name": "name",
            "propertyOfA": "12",
            "productype": "a"
        },
        {
            "id": "3",
            "name": "name",
            "propertyOfA": "1243",
            "productype": "a"
        },
        {
            "id": "1",
            "name": "name",
            "propertyOfB": "1243",
            "productype": "b"
        },
        {
            "id": "1",
            "name": "name",
            "propertyOfC": "1243",
            "propertyOfC2": "1243",
            "productype": "c"
        }
        ]
}

What i usually do is parse it into something like :


struct RequestData: Codable {
    let items: [Item]
}

enum ProductType: String, Codable {
    case a = "A"
    case b = "B"
    case c = "C"
}

struct Item: Codable {
    let id, name: String
    let propertyOfA: String?
    let productype: String
    let propertyOfB: String?
    let propertyOfC: String?
    let propertyOfC2: String?
}

But if this array keep growing, its gonna end up with a Item with a huge amount of optional property, so i want each object have its own dedicated class

adding some kind of bridge inside codable parsings,

what i want to archive is something like :

struct RequestData: Codable {
    let items: Items
}

struct items: Codable {
    let arrayA: [A]
    let arrayB = [B]
    let arrayC = [C]
}

struct A: Codable {
    let id: String,
    let name: String,
    let propertyOfA: String,
    let producttype: Productype
}

struct B {
...
}

struct C {
...
}

can i do this with Codable ?

0

1 Answer 1

5

A reasonable solution is an enum with associated values because the type can be determined by the productype key. The init method first decodes the productype with a CodingKey then in a switch it decodes (from a singleValueContainer) and associates the proper type/value to the corresponding case.

enum ProductType: String, Codable {
    case a, b, c
}

struct Root : Codable {
    let items : [Product]
}

struct ProductA : Codable {
    let id, name: String
    let productype: ProductType
    let propertyOfA : String
}

struct ProductB : Codable {
    let id, name: String
    let productype: ProductType
    let propertyOfB : String
}

struct ProductC : Codable {
    let id, name: String
    let productype: ProductType
    let propertyOfC, propertyOfC2 : String
}

enum Product : Codable {
    
    case a(ProductA), b(ProductB), c(ProductC)
    
    enum CodingKeys : String, CodingKey { case productype }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try  container.decode(ProductType.self, forKey: .productype)
        let singleContainer = try decoder.singleValueContainer()
        switch type {
            case .a : self = .a(try singleContainer.decode(ProductA.self))
            case .b : self = .b(try singleContainer.decode(ProductB.self))
            case .c : self = .c(try singleContainer.decode(ProductC.self))
        }
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
            case .a(let productA): try container.encode(productA)
            case .b(let productB): try container.encode(productB)
            case .c(let productC): try container.encode(productC)
        }
    }
}

And decode

let jsonString = """
{
    "items": [
        {
            "id": "1",
            "name": "name",
            "propertyOfA": "1243",
            "productype": "a"

        },
        {
            "id": "2",
            "name": "name",
            "propertyOfA": "12",
            "productype": "a"
        },
        {
            "id": "3",
            "name": "name",
            "propertyOfA": "1243",
            "productype": "a"
        },
        {
            "id": "1",
            "name": "name",
            "propertyOfB": "1243",
            "productype": "b"
        },
        {
            "id": "1",
            "name": "name",
            "propertyOfC": "1243",
            "propertyOfC2": "1243",
            "productype": "c"
        }
        ]
}
"""
do {
    let result = try JSONDecoder().decode(Root.self, from: Data(jsonString.utf8))
    print(result)
} catch { print(error)}

To read the enum values use also a switch.

Sign up to request clarification or add additional context in comments.

2 Comments

Hello, i appreciated this solution, it help a lot, is that possible to you to complete this with encoding part to make it conform to Codable ?
Please see the edit. To be able to encode the different types the key productype – by the way please consolidate the spelling of the key – must be added and decoded in ProductA-C

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.