Home > Software design >  How to deal with empty array to be sent as json in swift?
How to deal with empty array to be sent as json in swift?

Time:08-09

I'm trying to encode data but struggling to deal with empty array with no type as the API's I am working with needs me to send [] if there is no entry.

[
    {
        "RequestId": "5B6E36D9-8759-41BB-A0C0-EDFB116DFBB7",
        "DataSources": ["5B6E36D9-8759-41BB-A0C0-EDFB116DFBB7"],
        "GroupBy": [],
        "Filters": []
    }
]

above is the object json which I have to send.

struct ResponseElement: Encodable {
    let requestID: String
    let dataSources: [String]
    let groupBy: []
    let filters: []

    enum CodingKeys: String, CodingKey {
        case requestID = "RequestId"
        case dataSources = "DataSources"
        case groupBy = "GroupBy"
        case filters = "Filters"
    }
}

let data = ResponseElement(requestID: "5B6E36D9-8759-41BB-A0C0-EDFB116DFBB7",
                           dataSources: ["5B6E36D9-8759-41BB-A0C0-EDFB116DFBB7", ["5B6E36D9-8759-41BB-A0C0-EDFB116DFBB7]"],
                           groupBy: [],
                           filters: [])

let jsonEncoder = JSONEncoder()
let data = try! jsonEncoder.encode(data)

please note while creating data variable I have to pass groupBy, filters as empty array [], I have tried with [nil] which goes as [null] after encoding but it doesn't work in my case, it has to be []

how do I solve this please?

CodePudding user response:

If you know that the array will always be empty, the type of that is [Never]. A Never is a type that cannot be instantiated, so this array is necessarily empty.

Unfortunately, Never does not conform to Encodable. It should. It should conform to every protocol automatically (that would make Never a "bottom type," which is a good feature in a type system). That said, it's straightforward to provide the conformance:

extension Never: Encodable {
    public func encode(to encoder: Encoder) throws {
        // This line of code can never, ever run. It's impossible to
        // create a Never, so it's impossible to run its instance methods.
        fatalError()
    }
}

struct ResponseElement: Encodable {
    let requestID: String
    let dataSources: [String]
    let groupBy: [Never]
    let filters: [Never]

    ...
}

You may rightly feel uncomfortable extending a stdlib type to conform with a stdlib protocol. This is kind of fragile, since stdlib might create the conformance in the future, or some other package might do it, and there'd be a conflict. So you can do this explicitly by creating a type of your own that encodes an empty unkeyed container:

struct EmptyArray: Encodable {
    func encode(to encoder: Encoder) throws {
        encoder.unkeyedContainer()
    }
}

struct ResponseElement: Encodable {
    let requestID: String
    let dataSources: [String]
    let groupBy: EmptyArray
    let filters: EmptyArray
    ...
}

And finally, you can perform the encoding by hand and get rid of the unnecessary properties (this is how I'd do it myself):

struct ResponseElement: Encodable {
    let requestID: String
    let dataSources: [String]

    enum CodingKeys: String, CodingKey {
        case requestID = "RequestId"
        case dataSources = "DataSources"
        case groupBy = "GroupBy"
        case filters = "Filters"
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(requestID, forKey: .requestID)
        try container.encode(dataSources, forKey: .dataSources)
        container.nestedUnkeyedContainer(forKey: .groupBy)
        container.nestedUnkeyedContainer(forKey: .filters )
    }
}

CodePudding user response:

If this is just for encoding data and you never have to provide anything for this properties you could use any type you want for your property:

e.g.

struct ResponseElement: Encodable {
    let requestID: String
    let dataSources: [String]
    let groupBy: [String] = []
    let filters: [String] = []

    enum CodingKeys: String, CodingKey {
        case requestID = "RequestId"
        case dataSources = "DataSources"
        case groupBy = "GroupBy"
        case filters = "Filters"
    }
}

Result:

{
    "RequestId": "1",
    "GroupBy": [],
    "Filters": [],
    "DataSources": [
        "1",
        "2"
    ]
}
  • Related