使用可配置密钥的Swift 4 JSON解码 [英] Swift 4 JSON decode with configurable keys

查看:91
本文介绍了使用可配置密钥的Swift 4 JSON解码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Swift的新手,我需要使用一些 可配置密钥 解析JSON。
与我在这里看到的许多示例相反,开始解码操作之前,已知密钥,它们仅取决于传递给端点的某些参数。

I'm new to Swift and I need to parse a JSON with some configurable keys. Opposite to many examples I've seen here, the keys are known before the decode operation is started, they just depend on some parameters passed to endpoint.

示例:

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

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"
    },
    ...
]      

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

鉴于这些键在解码之前是已知的,所以我希望能够摆脱一些聪明的想法声明可解码结构和/或 CodingKeys ,而无需编写

Given that those keys are known before decoding, I was hoping to get away with some clever declaration of a Decodable structure and/or CodingKeys, without the need to write the

init(from decoder: Decoder)

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

Unfortunately, I was not able to come up with such a declaration.

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

Of course I don't want to write one Decodable/CodingKeys structure for every possible parameter value :-)

有什么建议吗?

推荐答案

除非您所有的JSON键都是编译时常量,否则编译器不能综合解码方法。但是,您可以做一些事情来减少手动解码的麻烦。

Unless all your JSON keys are compile-time constants, the compiler can't synthesize the decoding methods. But there are a few things you can do to make manual decoding a lot less cumbersome.

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

First, some helper structs and extensions:

/*
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)

这篇关于使用可配置密钥的Swift 4 JSON解码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆