使用可配置键进行 Swift 4 JSON 解码

2024-03-05

我是 Swift 新手,我需要用一些东西来解析 JSON可配置按键。 与我在这里看到的许多示例相反,密钥是已知的before解码操作开始,它们只依赖于传递给端点的一些参数。

Example:

https://some.provider.com/endpoint/?param=XXX

and

https://some.provider.com/endpoint/?param=YYY

将分别回答:

[
    {
        "fixed_key1": "value1",
        "fixed_key2": "value2",
        "variable_key_1_XXX": "some value",
        "variable_key_2_XXX": "some other value"
    },
    ...
]      

and

[
    {
        "fixed_key1": "value1",
        "fixed_key2": "value2",
        "variable_key_1_YYY": "some value",
        "variable_key_2_YYY": "some other value"
    },
    ...
]  

鉴于这些密钥是已知的解码前,我希望能够通过一些巧妙的声明来逃脱可解码结构和/或编码键,无需编写

init(from decoder: Decoder)

不幸的是,我无法提出这样的声明。

当然,我不想为每个可能的参数值编写一个 Decodable/CodingKeys 结构:-)

有什么建议吗?


除非所有 JSON 键都是编译时常量,否则编译器无法合成解码方法。但是您可以采取一些措施来使手动解码变得不那么麻烦。

首先,一些辅助结构和扩展:

/*
Allow us to initialize a `CodingUserInfoKey` with a `String` so that we can write:
    decoder.userInfo = ["param": "XXX"]

Instead of:
    decoder.userInfo = [CodingUserInfoKey(rawValue:"param")!: "XXX"]
*/
extension CodingUserInfoKey: ExpressibleByStringLiteral {
    public typealias StringLiteralType = String

    public init(stringLiteral value: StringLiteralType) {
        self.rawValue = value
    }
}

/*
This struct is a plain-vanilla implementation of the `CodingKey` protocol. Adding
`ExpressibleByStringLiteral` allows us to initialize a new instance of
`GenericCodingKeys` with a `String` literal, for example:
    try container.decode(String.self, forKey: "fixed_key1")

Instead of:
    try container.decode(String.self, forKey: GenericCodingKeys(stringValue: "fixed_key1")!)
*/
struct GenericCodingKeys: CodingKey, ExpressibleByStringLiteral {
    // MARK: CodingKey
    var stringValue: String
    var intValue: Int?

    init?(stringValue: String) { self.stringValue = stringValue }
    init?(intValue: Int) { return nil }

    // MARK: ExpressibleByStringLiteral
    typealias StringLiteralType = String
    init(stringLiteral: StringLiteralType) { self.stringValue = stringLiteral }
}

然后手动解码:

struct MyDataModel: Decodable {
    var fixedKey1: String
    var fixedKey2: String
    var variableKey1: String
    var variableKey2: String

    enum DecodingError: Error {
        case missingParamKey
        case unrecognizedParamValue(String)
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: GenericCodingKeys.self)

        // Decode the fixed keys
        self.fixedKey1 = try container.decode(String.self, forKey: "fixed_key1")
        self.fixedKey2 = try container.decode(String.self, forKey: "fixed_key2")

        // Now decode the variable keys
        guard let paramValue = decoder.userInfo["param"] as? String else {
            throw DecodingError.missingParamKey
        }

        switch paramValue {
        case "XXX":
            self.variableKey1 = try container.decode(String.self, forKey: "variable_key_1_XXX")
            self.variableKey2 = try container.decode(String.self, forKey: "variable_key_2_XXX")
        case "YYY":
            self.variableKey1 = try container.decode(String.self, forKey: "variable_key_1_YYY")
            self.variableKey2 = try container.decode(String.self, forKey: "variable_key_2_YYY")
        default:
            throw DecodingError.unrecognizedParamValue(paramValue)
        }
    }
}

最后是如何使用它:

let jsonData = """
[
    {
        "fixed_key1": "value1",
        "fixed_key2": "value2",
        "variable_key_1_XXX": "some value",
        "variable_key_2_XXX": "some other value"
    }
]
""".data(using: .utf8)!

// Supplying the `userInfo` dictionary is how you "configure" the JSON-decoding 
let decoder = JSONDecoder()
decoder.userInfo = ["param": "XXX"]
let model = try decoder.decode([MyDataModel].self, from: jsonData)

print(model)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用可配置键进行 Swift 4 JSON 解码 的相关文章

随机推荐