Декодирование необязательных значений в JSON — Swift 4

Я прочитал много руководств и искал ответы здесь, на SO;

Декодирование вложенного JSON с необязательными значениями Swift 4

Swift 4 Необязательно правильно декодирует JSON

Варианты декодирования не кажутся проблематичными, используя приведенный ниже код. Очевидно, я делаю что-то не так, но что!?

Часть моего кода:

struct ProductInfo: Codable {
    var name: String?
    let images: [Image]
    var ingredients: String?
    let origin: [Origin]
    let producer: Producer

    enum CodingKeys: String, CodingKey {
        case name = "Artikelbenamning"
        case images = "Bilder"
        case ingredients = "Ingrediensforteckning"
        case origin = "Tillverkningslander"
        case producer = "Varumarke"
    }

    init(from decoder: Decoder) throws {

        let values = try decoder.container(keyedBy: CodingKeys.self)

        name = try? values.decodeIfPresent(String.self, forKey: .name)!
        images = try values.decodeIfPresent([Image].self, forKey: .images)!
        ingredients = try? values.decodeIfPresent(String.self, forKey: .ingredients)!
        origin = try values.decodeIfPresent([Origin].self, forKey: .origin)!
        producer = try values.decodeIfPresent(Producer.self, forKey: .producer)!
    }
}

Приложение запрашивает базу данных продуктов. Следующий json возвращается, когда продукт не существует. Приложение вылетает при попытке декодировать нулевое имя ("Artikelbenamning в json") в init(from:).

{
    "GTIN": null,
    "TillverkarensArtikelnummer": null,
    "Artikelbenamning": null,
    "RegleratProduktnamn": null,
    "Forvaringsinstruktion": null,
    "Variabelmattsindikator": false,
    "Bruttovikt": null,
    "Bredd": null,
    "Djup": null,
    "Hojd": null,
    "Returemballage": false,
    "FarligtGodsKod": null,
    "FarligtGodsKlass": null,
    "FarligtGodsForpackningsgrupp": null,
    "GPCKod": null,
    "GiltigFROM": null,
    "Publiceringsdatum": null,
    "FakturerbarEnhet": false,
    "Slutdatum": null,
    "GiltighetsdatumPris": null,
    "Tillganglighetstidpunkt": null,
    "SistaTillganglighetstidpunkt": null,
    "SkapadDatum": null,
    "SenastAndradDatum": null,
    "Flampunkt": null,
    "KodBegransadMangd": null,
    "OfficiellTransportbenamning": null,
    "OspecificeradTransportbenamning": null,
    "TunnelrestriktionADR": null,
    "KlassificeringskodFarligtgods": null,
    "Transportkategori": null,
    "Konsumentartikel": false,
    "BestallningsbarForpackning": false,
    "RabattOlaglig": null,
    "Garantiloptid": 0,
    "Konsumentdatum": null,
    "Tjanst": false,
    "Sasongsindikator": null,
    "Engangskop": null,
    "AntalReturnerbaraEnheter": 0,
    "Staplingsriktning": null,
    "Staplingstyp": null,
    "MaxTransportTemperatur": 0.0,
    "MinTransportTemperatur": 0.0,
    "Anvandningsinstruktioner": null,
    "HallbarhetEfterOppning": 0,
    "Riskfras": null,
    "KodlistutgivareRiskfras": null,
    "Klassificeringssystem": null,
    "FarligtGodsBegransadMangd": null,
    "FarligtGodsOvrigInfo": null,
    "FarligtGodsSarbestammelser": null,
    "T3495_Artikelavisering": null,
    "T4032_TypAvUtgangsdatum": null,
    "T3742_ForstaLeveransdatum": null,
    "Undervarumarke": null,
    "Niva": null,
    "Produktbladslank": null,
    "KompletterandeProduktklass": null,
    "T4200_AllmänPubliceringstidpunkt": null,
    "T3848_TypAvTryckkanslighet": null,
    "Varningsetiketter": [],
    "Sasongskoder": [],
    "Produktklasser": [],
    "MaskinellMarkningar": [],
    "Bilder": [],
    "ReferenserTillAndraArtiklar": [],
    "MSRKritierier": [],
    "Kravspecifikationer": [],
    "Receptlinks": [],
    "Allergener": [],
    "Markningar": [],
    "Ingredienser": [],
    "Tillagningsinformation": [],
    "Tillverkningslander": [],
    "Naringsinfo": [],
    "Serveringsforslag": [],
    "Diettyper": [],
    "Tillagningsmetoder": [],
    "Farger": [],
    "VillkorForsaljning": [],
    "Varumarke": {
        "Varumarke": null,
        "AgareGLN": null,
        "AgareNamn": null,
        "Tillverkare": {
            "Namn": null,
            "EAN": null
        }
    },
    "Nettoinnehall": [],
    "Kontakter": [],
    "Faroangivelser": [],
    "Sakerhet": [],
    "Forpackningar": [],
    "Tillsatser": [],
    "Substanser": [],
    "Fangstzoner": [],
    "Marknadsbudskap": null,
    "KortMarknadsbudskap": null,
    "Komponenter": null
}

person Dan Abnormal    schedule 22.01.2019    source источник


Ответы (1)


API decodeIfPresent в данном случае бесполезен, поскольку ключ действительно существует.

существующий ключ со значением null и отсутствующий ключ — это две разные ситуации.

И никогда не принудительно разворачивайте в методе init(from decoder.

Есть два варианта:

  1. Удалите весь метод init, после чего необязательные члены структуры будут обрабатывать значения null.
  2. Используйте обычный decode и игнорируйте ошибку

    name = try? values.decode(String.self, forKey: .name)
    
person vadian    schedule 22.01.2019
comment
Спасибо за ваш ответ. Извините, новичок, что именно вы имеете в виду под вашим вариантом 1? - person Dan Abnormal; 22.01.2019
comment
Я имею в виду то, что я написал: удалить метод init(from decoder. Расширение протокола предоставляет синтезированный. - person vadian; 22.01.2019
comment
Ok. Я постараюсь вернуться к вам. - person Dan Abnormal; 22.01.2019
comment
Интересно, а вы не могли бы мне сказать, почему он разбился? - person Dan Abnormal; 22.01.2019
comment
Поскольку try? возвращает необязательный параметр, и если строки (или одного из других типов) нет, и вы принудительно разворачиваете ее, происходит сбой. Пожалуйста, прочитайте третий абзац еще раз. - person vadian; 22.01.2019