使用 Swift for iOS 为嵌套对象自定义 JSON 解码器

问题描述 投票:0回答:1

使用 iOS 版 Swift,我需要解码 JSON 响应。它包含一个嵌套对象,需要父对象的值 (

type
) 才能进行解码。

JSON结构:

{
    "name":"My email",
    "type":"Email",
    "content":{
        "attributes":{
            "email":"[email protected]"
        }
    }
}

{
    "name":"My phone number",
    "type":"PhoneNumber",
    "content":{
        "attributes":{
            "indicator":"+33",
            "national_number":"123456789",
        }
    }
}

attributes
内容取决于
type
。因此
content
嵌套对象需要知道
type
才能解码
attributes

结构:

struct Response: Decodable {
    let name: String
    let type: String
    let content: ContentResponse?

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        type = try container.decode(String.self, forKey: .type)
        // !!!!!!! Here I need to pass the "type" value to content decoding
        content = try container.decodeIfPresent(ContentResponse.self, forKey: .content)
    }
}

struct ContentResponse: Decodable {
    let issued: Date
    let attributes: DocumentResponse?

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        issued = try container.decode(Date.self, forKey: .issued)
        if container.contains(.attributes) {
            // !!!!!!! Here I can't access the "type" value from parent object
            switch Type.fromString(type: type) {
            case .email:
                attributes = try container.decode(DocumentEmailResponse.self, forKey: .attributes)
            case .phoneNumber:
                attributes = try container.decode(DocumentPhoneNumberResponse.self, forKey: .attributes)
            default:
                // Unsupported type 
                attributes = nil
            }
        } else {
            attributes = nil
        }
    }
}

class DocumentResponse: Decodable {}

class DocumentEmailResponse: DocumentResponse {
    let email: String
}

class DocumentPhoneNumberResponse: DocumentResponse {
    let indicator: String
    let nationalNumber: String
}

如您所见,

init(from decoder: Decoder)
ContentResponse
需要知道
type
才能知道使用哪个类来进行
attributes
解码。

如何将

type
上解码的
Response
传递给嵌套对象
ContentResponse
进行解码?

不工作#1

我在这里找到了一个解决方案https://www.andyibanez.com/posts/the-mysterious-codablewithconfiguration-protocol/使用

CodableWithConfiguration
decodeIfPresent(_:forKey:configuration:)
,但它针对iOS 15+,而我针对iOS 13+ .

不工作#2

我可以在

userInfo
中使用
decoder
init(from decoder: Decoder)
,但它是只读的:

var userInfo: [CodingUserInfoKey : Any] { get }
ios json swift decoding decoder
1个回答
0
投票

我会为此使用枚举,一个用于内容类型,一个用于保存解码的内容。

首先是

type

的枚举
enum ContentType: String, Codable {
    case email = "Email"
    case phone = "PhoneNumber"
    case none
}

然后是

content

enum Content: Codable {
    case email(DocumentEmailResponse)
    case phone(DocumentPhoneNumberResponse)
    case none
}

我将

Content
中使用的类型更改为结构

struct DocumentEmailResponse: Codable {
    let email: String
}

struct DocumentPhoneNumberResponse: Codable {
    let phoneNumber: String
}

然后所有自定义解码都参与

Response
,其中
Content
值使用嵌套容器进行解码。

struct Response: Codable {
    let name: String
    let type: ContentType
    let content: Content

    enum ContentCodingKeys: String, CodingKey {
        case attributes
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        type = try container.decode(ContentType.self, forKey: .type)
        let contentContainer = try container.nestedContainer(keyedBy: ContentCodingKeys.self, forKey: .content)
        switch type {
        case .email:
            let value = try contentContainer.decode(DocumentEmailResponse.self, forKey: .attributes)
            content = .email(value)
        case .phone:
            let value = try contentContainer.decode(DocumentPhoneNumberResponse.self, forKey: .attributes)
            content = .phone(value)
        default:
            content = .none
        }
    }
}

这两个枚举都包含

none
情况,但根据可能可选或不可选的内容以及您可能希望在可能的情况下删除它们的个人喜好,我不完全确定哪些值以及何时可以为 nil。

© www.soinside.com 2019 - 2024. All rights reserved.