Объявить пустой словарь в кодируемом классе

#ios #swift #codable #decodable #encodable

#iOS #swift #кодируемый #декодируемый

Вопрос:

Я хочу отправить пустой словарь следующим образом

 “visitor_attrs”: {}
  

Я пытаюсь реализовать пустой словарь в классе. В декодере я получаю предупреждение:

Ни один из кандидатов ‘decode’ не выдает ожидаемый контекстный результат типа ‘Dictionary’

Как я могу это сделать?

 var data: String
var event: String
var visitorAttrs: Dictionary<String, Any>

init(data: String, event: String) {
    self.data = data
    self.event = event
    self.visitorAttrs = [:]
}

private enum CodingKeys: String, CodingKey {
    case data
    case event
    case visitorAttrs = "visitor_attrs"
}

required public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.data = try container.decode(String.self, forKey: .data)
    self.event = try container.decode(String.self, forKey: .event)
    self.visitorAttrs = try container.decode(Dictionary<String:Any>.self, forKey: .visitorAttrs)
}

public func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(self.data, forKey: .data)
    try container.encode(self.event, forKey: .event)
    try container.encode(self.visitorAttrs, forKey: .visitorAttrs)
} 
  

Ответ №1:

Я предполагаю, что, поскольку visitorAttrs пуст в вашем ответе JSON, из него не удается извлечь словарь, поскольку его нет.

У вас есть два варианта (насколько я знаю)

  1. Сделайте свойство visitorAttrs необязательным, таким образом, если у вас ничего нет, оно все равно сможет правильно декодироваться, или,

  2. Установите значение visitorAttrs в пустой словарь, если не удается его декодировать

 let visitorAttrs = try? container.decode(Dictionary<String:Any>.self, forKey: .visitorAttrs)
self.visitorAttrs = visitorAttrs ?? [:]
  

Ответ №2:

Ошибка вызвана наличием любого значения as в вашем словаре, и любое из них не поддается декодированию, замените его на String (или любой другой тип данных, который у вас есть), и это должно сработать.

 var visitorAttrs: Dictionary<String, String>
  

Исправьте эту проблему, и вы получите ожидаемое поведение

 let item = Item(data: "data", event: "event") //I gave your class the name Item
let encoder = JSONEncoder()

do {
    let data = try encoder.encode(item)
    if let str = String(data: data, encoding: .utf8) {
        print(str)
    }
} catch {
    print(error)
}
  

Вывод

{«event»: «событие», «visitor_attrs»:{},»data»: «данные»}

Комментарии:

1. Другим вариантом было бы использовать здесь пустой Codable тип. Нравится struct Stub: Codable {} . Это тоже будет работать, и не нужно предоставлять нерелевантную информацию о типе, такую как <String, String> .

2. @user28434 Да, но нет, если он иногда будет содержать данные, полный вариант использования неясен.

3. Действительно, но если он содержит данные, все равно может быть лучше использовать Codable class , только со всеми полями, равными Optional . Я сомневаюсь, что все значения будут одного типа.