Codable struct - перечисление при отсутствии ключа при разборе json - PullRequest
0 голосов
/ 10 октября 2018

Я пытаюсь проанализировать данные JSON из API.Некоторые ключи возвращают несколько типов или иногда отсутствуют.Все работает нормально, пока ключ (значение) здесь.Но даже если я объявил это необязательным в моей структуре, выдается ошибка, если ключ отсутствует.Ошибка выдается из блока init в enum MyValue.

Мой код выглядит так:

struct ServiceUnit: Codable {
    let description,id: String?
    let group, groupDescription:String?
    let name: String?
    let value: MyValue?

    enum CodingKeys: String, CodingKey {
        case description = "Description"
        case group = "Group"
        case groupDescription = "GroupDescription"
        case id = "Id"
        case name = "Name"
        case value = "Value"
    }
}

enum MyValue: Codable {
    case string(String)
    case innerItem(InnerItem)
    case double(Double)
    case int(Int)
    case bool(Bool)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
            if let string = try? container.decode(String.self)  {
                self = .string(string)
                return
            }
            if let innerItem = try? container.decode(InnerItem.self) {
                self = .innerItem(innerItem)
                return
            }
            if let double = try? container.decode(Double.self) {
                self = .double(double)
                return
            }
            if let int = try? container.decode(Int.self) {
                self = .int(int)
                return
            }
            if let bool = try? container.decode(Bool.self){
                self = .bool(bool)
            }
            throw DecodingError.typeMismatch(MyValue.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for MyValue"))
        }  
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .string(let x):
            try container.encode(x)
        case .innerItem(let x):
            try container.encode(x)
        case .double(let x):
            try container.encode(x)
        case .int(let x):
            try container.encode(x)
        case .bool(let x):
            try container.encode(x)
        }


    }
}

struct InnerItem: Codable {
    let type, id, name: String?

    enum CodingKeys: String, CodingKey {
        case type = "__type"
        case id = "Id"
        case name = "Name"
    }
}

И JSON выглядит так:

{
    "Description": null,
    "Group": "Beskrivning av enheten",
    "GroupDescription": null,
    "Id": "Description",
    "Name": "Mer om enheten",
    "Value": "Förskolans inriktningen omfattar barns flerspråkighet och integration. Förskolan ligger i Järva med närhet till parker, skog och natur och vi tillbringar mycket tid där. Vi ger barnen möjligheter till upplevelser och inlärning där deras nyfikenhet och upptäckarlust får styra. Förskolan använder sig av ett språkutvecklande och utforskande arbetssätt under hela förskoledagen. Vi arbetar med pedagogisk dokumentation, vi observerar och reflekterar kring arbetssättet för att utveckla verksamheten framåt. Vi dokumenterar vad barnen gör, så att det blir synligt vad och hur barnen lär sig. \r\nPedagogerna skapar förutsättningar för barnens utveckling genom att, i en tillåtande miljö, ge dem möjligheter att få arbeta med material som inbjuder till rolig och utforskande lek. Förskolan har ett eget tillagningskök som erbjuder näringsrik och spännande mat."
},
{
    "Description": null,
    "Group": "Relaterade dokument",
    "GroupDescription": null,
    "Id": "Documents",
    "Name": "Filer",
    "Value": [
        {
            "__type": "FileInfo",
            "Id": "040e5147-35a4-488e-8356-f47dad1fdc68",
            "Name": "Forskolan_Umma___Hyppingeplan_-_Foraldrar_Forskola.pdf"
        },
        {
            "__type": "FileInfo",
            "Id": "41202e0d-b642-40d0-b2c4-5af871c5a028",
            "Name": "Spånga-Tensta_033527 Umma Förskola, Hyppingeplan 2017.pdf"
        }
    ]
}

Я пыталсячтобы сделать это в init в enum MyValue:

 if container.decodeNil() == false {
   ...
 }else{
   self = .string("")
   return
 }

Я также пытался это сделать в структуре init, но она также не работает:

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        value = try! container.decodeIfPresent(MyValue.self, forKey: .value)
        description = try container.decode(String.self, forKey: .description)
        id = try container.decode(String.self, forKey: .id)
        group = try container.decode(String.self, forKey: .group)
        groupDescription = try container.decode(String.self, forKey: .groupDescription)
        name = try container.decode(String.self, forKey: .name)
    }

Ошибка:

Ошибка typeMismatch (stockholmsParks.detailViewController.MyValue, Swift.DecodingError.Context (codingPath: [_JSONKey (stringValue: «Индекс 25», intValue: 25), CodingKeys (stringValue: «Value»)nil)], debugDescription: «Неверный тип для MyValue», underError: nil))

Кто-нибудь знает, как обрабатывать недостающие ключи в этой ситуации?

1 Ответ

0 голосов
/ 10 октября 2018

Проблема в том, что вы присваиваете ключу Value значение, которое не String, Bool, Int, Double или InnerItem, поэтому возврат не срабатывает, и управление достигает

 throw DecodingError.typeMismatch(MyValue.self,
  DecodingError.Context(codingPath: decoder.codingPath,
    debugDescription: "Wrong type for MyValue"))     

Примечание: Неправильный тип для MyValue в debugDescription

, который печатает ошибку, которую вы видите в консоли, кстати, сделав ее необязательной, будетпередать только если он выглядит как

«Значение»: null

или ключ / значение полностью не существует

...