3

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?

nitpaxy
  • 67
  • 10
  • on the cases where the server does send down values for `groupBy` and `filters`, what kind of values are they? are they a list of integers? strings? – kbunarjo Aug 08 '22 at 14:06
  • I have to send down values to the server and the values are strings, so its array of strings. and when I am not sending anything in that case it has to be empty. – nitpaxy Aug 08 '22 at 16:02

2 Answers2

3

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 )
    }
}
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
1

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"
    ]
}
burnsi
  • 6,194
  • 13
  • 17
  • 27