Swift: константа в определении шаблона - PullRequest
0 голосов
/ 20 мая 2019

Я работаю с бэкэнд-разработчиком, который любит инкапсулировать тела json в другой объект, такой как данные:

Пример:

GET: / пользователь / текущий:

{
  data: {
          firstName: "Evan",
          lastName: "Stoddard"
        }
}

Я просто хотел бы просто вызвать json decode для ответа, чтобы получить созданную мной структуру User, но для добавленного объекта данных требуется другая структура. Чтобы обойти это, я создал шаблон общего класса:

struct DecodableData<DecodableType:Decodable>:Decodable {

    var data:DecodableType

}

Теперь я могу получить полезную нагрузку json и, если я хочу получить структуру User, просто получить свойство data моего шаблона:

let user = JSONDecoder().decode(DecodableData<User>.self, from: jsonData).data

Это все прекрасно и прекрасно, пока иногда ключ data не всегда data.

Мне кажется, что это, скорее всего, довольно тривиальные вещи, но есть ли способ добавить параметр в определение шаблона, чтобы я мог изменить ключи кодирования перечисления, так как этот ключ данных может измениться?

Что-то вроде следующего?

struct DecodableData<DecodableType:Decodable, Key:String>:Decodable {

    enum CodingKeys: String, CodingKey {
        case data = Key
    }

    var data:DecodableType

}

Таким образом, я могу передать целевой декодируемый класс вместе с ключом, который инкапсулирует этот объект.

Ответы [ 2 ]

2 голосов
/ 21 мая 2019

Нет необходимости в кодировании ключей. Вместо этого вам нужен простой контейнер, который анализирует JSON как словарь, содержащий ровно одну пару ключ-значение, отбрасывая ключ.

struct Container<T>: Decodable where T: Decodable {
    let value: T

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let dict = try container.decode([String: T].self)

        guard dict.count == 1 else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "expected exactly 1 key value pair, got \(dict.count)")
        }

        value = dict.first!.value
    }
}

Если JSON пуст или содержит более одной пары ключ-значение, возникает исключение.

Предполагая простую структуру, такую ​​как

struct Foo: Decodable, Equatable {
    let a: Int
}

вы можете разобрать его независимо от ключа:

let foo1 = try! JSONDecoder().decode(
    Container<Foo>.self,
    from: #"{ "data": { "a": 1 } }"#.data(using: .utf8)!
).value

let foo2 = try! JSONDecoder().decode(
    Container<Foo>.self,
    from: #"{ "doesn't matter at all": { "a": 1 } }"#.data(using: .utf8)!
).value

foo1 == foo2 // true

Это также работает для ответов JSON, которые имеют null в качестве значения, и в этом случае вам необходимо проанализировать его как необязательный тип:

let foo = try! JSONDecoder().decode(
    Container<Foo?>.self,
    from: #"{ "data": null }"#.data(using: .utf8)!
).value // nil
0 голосов
/ 20 мая 2019

Попробуйте что-то вроде этого:

struct GenericCodingKey: CodingKey {
    var stringValue: String

    init(value: String) {
        self.stringValue = value
    }

    init?(stringValue: String) {
        self.stringValue = stringValue
    }

    var intValue: Int?

    init?(intValue: Int) {
        return nil
    }
}

struct DecodableData<DecodableType: CustomDecodable>: Decodable {

    var data: DecodableType

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: GenericCodingKey.self)
        data = try container.decode(DecodableType.self, forKey: GenericCodingKey(value: DecodableType.dataKey))
    }
}

protocol CustomDecodable: Decodable {
    static var dataKey: String { get }
}

extension CustomDecodable {
    static var dataKey: String {
        return "data" // This is your default
    }
}


struct CustomDataKeyStruct: CustomDecodable {
    static var dataKey: String = "different"
}

struct NormalDataKeyStruct: CustomDecodable {
    //Change Nothing
}
...