可编码结构-解析JSON时缺少键时的枚举

时间:2018-10-10 20:30:56

标签: ios swift swift4 codable

我尝试从API解析JSON数据。一些键返回多种类型,或者有时不存在。只要键(值)在这里,一切都可以正常工作。但是,即使我在结构中将其声明为可选的,如果缺少该键,也会引发错误。错误是从枚举MyValue中的init块引发的。

我的代码如下:

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的init中这样做:

 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 :“ MyValue类型错误”,underlyingError:nil))

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

1 个答案:

答案 0 :(得分:1)

问题是您为Value键分配了一个不是StringBoolIntDouble或{{1} },这样返回就不会命中,控制权会到达

InnerItem

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

中MyValue类型错误

它会打印出您在控制台中看到的错误,顺便说一句,使其变为可选状态,只有在看起来像这样时才会通过

  

“值”:空

或键/值完全不存在