当前位置: 首页 > 面试题库 >

动态JSON解码Swift 4

戚明朗
2023-03-14
问题内容

我正在尝试在Swift 4中解码以下JSON:

{
    "token":"RdJY3RuB4BuFdq8pL36w",
    "permission":"accounts, users",
    "timout_in":600,
    "issuer": "Some Corp",
    "display_name":"John Doe",
    "device_id":"uuid824fd3c3-0f69-4ee1-979a-e8ab25558421"
}

问题是,JSON中的最后2个元素(display_namedevice_id)可能存在或可能不存在,或者这些元素可能被命名为完全不同但仍未知的名称,即"fred": "worker", "hours" : 8

所以,我想要实现的解码是已知的东西,即tokenpermissiontimeout_inissuer任何其他元素(display_namedevice_id等),将它们放入一个字典。

我的结构如下所示:

struct AccessInfo : Decodable
{
    let token: String
    let permission: [String]
    let timeout: Int
    let issuer: String
    let additionalData: [String: Any]

    private enum CodingKeys: String, CodingKey
    {
        case token
        case permission
        case timeout = "timeout_in"
        case issuer
    }

    public init(from decoder: Decoder) throws
    {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        token = container.decode(String.self, forKey: .token)
        permission = try container.decodeIfPresent(String.self, forKey: .permission).components(separatedBy: ",")
        timeout = try container.decode(Int.self, forKey: . timeout)
        issuer = container.decode(String.self, forKey: .issuer)

        // This is where I'm stuck, how do I add the remaining
        // unknown JSON elements into additionalData?
    }
}

// Calling code, breviated for clarity
let decoder = JSONDecoder()
let accessInfo = try decoder.decode(AccessInfo.self, from: data!)

如果有人可以提供一些指导,那么我能够解码已知结构的一部分,其中JSON也可以包含动态信息。

谢谢


问题答案:

受@matt注释的启发,这是我所使用的完整示例。我扩展了,KeyedDecodingContainer以对未知密钥进行解码,并提供一个参数以过滤出已知密钥CodingKeys

样本JSON

{
    "token":"RdJY3RuB4BuFdq8pL36w",
    "permission":"accounts, users",
    "timout_in":600,
    "issuer": "Some Corp",
    "display_name":"John Doe",
    "device_id":"uuid824fd3c3-0f69-4ee1-979a-e8ab25558421"
}

迅捷结构

struct AccessInfo : Decodable
{
    let token: String
    let permission: [String]
    let timeout: Int
    let issuer: String
    let additionalData: [String: Any]

    private enum CodingKeys: String, CodingKey
    {
        case token
        case permission
        case timeout = "timeout_in"
        case issuer
    }

    public init(from decoder: Decoder) throws
    {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        token = container.decode(String.self, forKey: .token)
        permission = try container.decode(String.self, forKey: .permission).components(separatedBy: ",")
        timeout = try container.decode(Int.self, forKey: . timeout)
        issuer = container.decode(String.self, forKey: .issuer)

        // Additional data decoding
        let container2 = try decoder.container(keyedBy: AdditionalDataCodingKeys.self)
        self.additionalData = container2. decodeUnknownKeyValues(exclude: CodingKeys.self)
    }
}

private struct AdditionalDataCodingKeys: CodingKey
{
    var stringValue: String
    init?(stringValue: String)
    {
        self.stringValue = stringValue
    }

    var intValue: Int?
    init?(intValue: Int)
    {
        return nil
    }
}

KeyedDecodingContainer扩展

extension KeyedDecodingContainer where Key == AdditionalDataCodingKeys
{
    func decodeUnknownKeyValues<T: CodingKey>(exclude keyedBy: T.Type) -> [String: Any]
    {
        var data = [String: Any]()

        for key in allKeys
        {
            if keyedBy.init(stringValue: key.stringValue) == nil
            {
                if let value = try? decode(String.self, forKey: key)
                {
                    data[key.stringValue] = value
                }
                else if let value = try? decode(Bool.self, forKey: key)
                {
                    data[key.stringValue] = value
                }
                else if let value = try? decode(Int.self, forKey: key)
                {
                    data[key.stringValue] = value
                }
                else if let value = try? decode(Double.self, forKey: key)
                {
                    data[key.stringValue] = value
                }
                else if let value = try? decode(Float.self, forKey: key)
                {
                    data[key.stringValue] = value
                }
                else
                {
                    NSLog("Key %@ type not supported", key.stringValue)
                }
            }
        }

        return data
    }
}

呼叫码

let decoder = JSONDecoder()
let accessInfo = try decoder.decode(AccessInfo.self, from: data!)

print("Token: \(accessInfo.token)")
print("Permission: \(accessInfo.permission)")
print("Timeout: \(accessInfo.timeout)")
print("Issuer: \(accessInfo.issuer)")
print("Additional Data: \(accessInfo.additionalData)")

输出量

Token: RdJY3RuB4BuFdq8pL36w
Permission: ["accounts", "users"]
Timeout: 600
Issuer: "Some Corp"
Additional Data: ["display_name":"John Doe", "device_id":"uuid824fd3c3-0f69-4ee1-979a-e8ab25558421"]


 类似资料:
  • 输入数据上有一个例子。 这是我在向服务器发出适当请求后收到的数据示例。我得到了这样的数据。如何序列化此类数据?由于嵌套结构列表的动态名称,我的尝试失败了。如何正确处理这种嵌套动态结构?

  • 问题内容: 在输入数据上有一个示例。 这是在向服务器发出适当请求后我收到的数据的示例。我得到了这样的数据。此类数据如何序列化?由于嵌套结构列表的动态名称,我的尝试失败了。如何正确处理此类嵌套的动态结构? 问题答案: 使用地图(类型为)在JSON中对对象进行建模: 然后拆封: 将导致(在Go Playground上尝试): {Status:OK StatusCode:100 Sms:map [790

  • 问题内容: 我如何解析这个json对象: 我可以有N个端口,每个端口的值始终是key:value对。 到目前为止,我已经尝试过了: 有了这个我得到键(0,1),但值是空的。 我也尝试过这个: 但也不行。 这就是我解码json对象的方式: 问题答案: 使用此类型: 游乐场的例子 笔记: 字段名称非常匹配。我使用字段名称“ Ports”来匹配JSON文本中使用的名称。 Go类型在JSON中应具有相同级

  • 问题内容: 我有一个json对象是这样的: 我试图这样解析: 但是我不知道如何访问动态名称。我们如何解析这样的JSON -注意-Ya的 所有值都带有引号,例如:“ Yg&R_” 问题答案: 试试这个动态的JSON解析器

  • 问题内容: 我有动态JSON,下面是示例:http : //pastebin.com/QMWRZTrD 如何使用翻新解析? 我没有生成POJO类,因为我有诸如“ 5411”和“ 5412”之类的动态字段。 编辑 : 我通过使用Map来解决它,因为第一个值始终是整数,第二个是对象列表。 问题答案: 如果使用随机键,则可以用来序列化和反序列化。

  • 我使用flink动态分析json类型的数据,对keyby和给定的列求和,在我的mapFunction中,我将json转换为case类,但结果流没有在keyby函数中得到编译器,在线程“main”org.apache.flink.api.common.InvalidProgramException中得到错误。我的代码如下所示 我如何将json转换为case类或tuple?