Codable 结构 - 解析时缺少密钥时的枚举 json

Codable struct - enum when key missing when parsing json

我尝试解析来自 API 的 JSON 数据。一些键 return 多种类型或有时不存在。只要键(值)在这里,一切正常。但即使我在我的结构中将其声明为可选,如果密钥不存在,也会抛出错误。该错误是从枚举 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"
        }
    ]
}

我尝试在枚举 MyValue 的初始化中这样做:

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

我在struct 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)
    }

错误是:

Err typeMismatch(stockholmsParks.detailViewController.MyValue, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 25", intValue: 25), CodingKeys(stringValue: "Value", intValue: nil)], debugDescription: "Wrong type for MyValue", underlyingError: nil))

有人知道在这种情况下如何处理丢失的钥匙吗?

问题是您为 Value 键分配的值不是 StringBoolIntDoubleInnerItem 所以 return 没有命中并且控制到达

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

注意: debugDescription

中 MyValue 的类型错误

它会打印出您在控制台中看到的错误,顺便说一句,只有当它看起来像

时,将其设为可选才会通过

"Value":null

或者 key/value 完全不存在