Swift JSONDecoder — ключи кодирования не работают с подчеркиванием

#ios #json #swift #decoding #codable

#iOS #json #swift #декодирование #кодируемый

Вопрос:

Может кто-нибудь, пожалуйста, объяснить, почему код в части A работает, а B не нравится. Это сбило меня с толку.

РАБОТАЕТ

 struct Coded : Codable, Hashable {  
  public let avar1: String
  public let avar2: String

  enum CodingKeys: String, CodingKey {
    case avar1 = "avar1"
    case avar2 = "avar2"
  }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    print (container.allKeys)
    avar1 = try container.decode(String.self, forKey: .avar1)
    avar2 = try container.decode(String.self, forKey: .avar2)
  }
}

let JSONStr = """
{
  "avar1": "This is a string",
  "avar2": "This is a string2",
}
"""

if let jsdata = JSONStr.data(using: .utf8) {
  let decoder = JSONDecoder()
  decoder.keyDecodingStrategy = .convertFromSnakeCase
  let aobj: Coded? = try? decoder.decode(Coded.self, from: jsdata)
  print (aobj ?? "No object")
}
  

ВЫВОД

 [CodingKeys(stringValue: "avar1", intValue: nil), CodingKeys(stringValue: "avar2", intValue: nil)]
Coded(avar1: "This is a string", avar2: "This is a string2")
  

НЕ РАБОТАЕТ

 struct Coded : Codable, Hashable {  
  public let avar1: String
  public let avar2: String

  enum CodingKeys: String, CodingKey {
    case avar1 = "avar1"
    case avar2 = "avar_2"
  }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    print (container.allKeys)
    avar1 = try container.decode(String.self, forKey: .avar1)
    avar2 = try container.decode(String.self, forKey: .avar2)
  }
}

let JSONStr = """
{
  "avar1": "This is a string",
  "avar_2": "This is a string2",
}
"""

if let jsdata = JSONStr.data(using: .utf8) {
  let decoder = JSONDecoder()
  decoder.keyDecodingStrategy = .convertFromSnakeCase
  let aobj: Coded? = try? decoder.decode(Coded.self, from: jsdata)
  print (aobj ?? "No object")
}
  

ВЫВОД

 [CodingKeys(stringValue: "avar1", intValue: nil)]
No object
  

Вторая функция будет отображать только ключ кодирования без подчеркивания. Но как только я удаляю подчеркивание, у него появляется ключ кодирования в allKeys…

Swift 4.2 — Xcode 10.2.

Есть идеи?

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

1. Я, наконец, получил эту работу после закрытия моей игровой площадки и запуска ее снова. Похоже, это может быть проблема с интерпретатором… Это чрезвычайно странно.

2. Похоже, что большие наборы данных использовали .convertFromSnakeCase, что привело к исчезновению ключей кодирования во время декодирования.

Ответ №1:

.convertFromSnakeCase преобразует переменные snake_cased в camelCase перед обращением к CodingKeys .

Если вы хотите указать, CodingKeys вы должны использовать преобразованное значение в вашем НЕ РАБОЧЕМ примере

 enum CodingKeys: String, CodingKey {
    case avar1 = "avar1"
    case avar2 = "avar2"
}
  

Но это иллюстрирует бессмысленность CodingKeys . Поэтому подумайте наоборот и воспользуйтесь стратегией декодирования ключей.

Вместо удаления .convertFromSnakeCase удалите CodingKeys и инициализатор.

И всегда ловите возможные Decoding ошибки.

 struct Coded : Codable {
    public let avar1: String
    public let avar2: String
}

let jsonStr = """
{
"avar1": "This is a string",
"avar_2": "This is a string2",
}
"""

let jsdata = Data(jsonStr.utf8)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
    let aobj = try decoder.decode(Coded.self, from: jsdata)
    print(aobj)
} catch { print(error) }
  

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

1. Спасибо, что поделились информацией о том, что ключи преобразуются в верблюжий регистр перед доступом к ключам кодирования. Мое личное предпочтение было бы удалить convertFromSnakeCase, потому что его использование запутало привязку ключей к именам переменных в JSON.

Ответ №2:

Я исправил эту проблему. Это связано с проблемой, возникающей при использовании Snake Case. В некоторых ситуациях это может вызвать проблемы. Что это такое и почему они происходят, я не уверен. Но если у вас есть JSONDecoder с включенным запуском Snake Case. Это удалит коды клавиш для элементов, которые закодированы с использованием регистра snake.

Удалив следующее из моего JSONDecoder, я смог устранить проблему.

Пожалуйста, удалите следующее и принудительно введите строки кодирования в регистр snake, если у вас установлен этот параметр, и принудительно используйте стратегию JSONDecoder с помощью .convertFromSnakeCase, это удалит строки ключей кодирования и сломается во время декодирования.

Если ваши ключи кодирования включают

 codingKey = "a_json_var"
  

Если вы добавите convertFromSnakeCase , это полностью удалит ключ кодирования. Итак, не указывайте текстовую строку в регистре ключа кодирования в регистре snake, иначе она сломается.

Удалить ->

 decoder.keyDecodingStrategy = .convertFromSnakeCase
  

Если вы хотите правильно указать точное текстовое название ключа кодирования.