Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
动态JSON解码Swift 4_Json_Swift_Dynamic_Decodable - Fatal编程技术网

动态JSON解码Swift 4

动态JSON解码Swift 4,json,swift,dynamic,decodable,Json,Swift,Dynamic,Decodable,我试图在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

我试图在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个元素(
显示\u名称
设备\u id
)可能存在,也可能不存在,或者这些元素的名称可能完全不同,但仍然未知,即
“fred”:“worker”,“hours”:8

因此,我试图实现的是解码已知的内容,即
令牌
权限
超时
颁发者
,以及任何其他元素(
显示名称
设备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也可能包含动态信息的部分就是我的目标


谢谢

这个问题实际上是重复的。一旦您了解了构建底层最小CodingKey应用程序结构作为编码密钥的诀窍,就可以将其用于任何字典

在本例中,您将使用键控容器的
allKeys
获取未知的JSON字典键

为了演示,我将仅限于JSON字典中完全未知的部分。想象一下这个JSON:

let j = """
{
    "display_name":"John Doe",
    "device_id":"uuid824fd3c3-0f69-4ee1-979a-e8ab25558421"
}
"""
let jdata = j.data(using: .utf8)!
假设我们不知道字典里有什么,除了它有字符串键和字符串值之外。所以我们想要解析
jdata
,而不知道它的键是什么

因此,我们有一个由一个字典属性组成的结构:

struct S {
    let stuff : [String:String]
}
现在的问题是如何将该JSON解析为该结构——即,如何使该结构符合可解码性并处理该JSON

以下是方法:

struct S : Decodable {
    let stuff : [String:String]
    private struct CK : CodingKey {
        var stringValue: String
        init?(stringValue: String) {
            self.stringValue = stringValue
        }
        var intValue: Int?
        init?(intValue: Int) {
            return nil
        }
    }
    init(from decoder: Decoder) throws {
        let con = try! decoder.container(keyedBy: CK.self)
        var d = [String:String]()
        for key in con.allKeys {
            let value = try! con.decode(String.self, forKey:key)
            d[key.stringValue] = value
        }
        self.stuff = d
    }
}
现在我们分析:

let s = try! JSONDecoder().decode(S.self, from: jdata)
我们得到一个S实例,它的
stuff
就是这个字典:

["device_id": "uuid824fd3c3-0f69-4ee1-979a-e8ab25558421", "display_name": "John Doe"]

这正是我们想要的结果。

受@matt comments的启发,这是我随附的完整示例。我扩展了
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"
}
Swift结构

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
    }
}
输出

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

您的JSON无效。请提供真实的JSON。重复的,如果未知数据不总是类型为
[String:String]
?这是一个不同的问题,在堆栈溢出问题上也得到了彻底的解决。感谢您的回复。我审阅并编写了@matt works的示例代码。当在单个解析/解码操作中组合已知密钥和未知密钥时,实现变得棘手,因为您希望对容器进行两次解码。。。或者作为一种通用方法进行解码,并手动分配已知键,将剩余的未知键放入字典
[String:String]
中。您进入两次,一次使用已知键,正如您已经展示的那样,另一次使用未知键,正如我所做的那样。我需要把整个事情说清楚吗?有人能提供关于已知密钥但未知数据类型的问题的链接吗?经过几个小时的搜索,这终于帮了我的忙!我不明白的一点是下面这行:
if keyedBy.init(stringValue:key.stringValue)==nil
。为什么返回零?
Token: RdJY3RuB4BuFdq8pL36w
Permission: ["accounts", "users"]
Timeout: 600
Issuer: "Some Corp"
Additional Data: ["display_name":"John Doe", "device_id":"uuid824fd3c3-0f69-4ee1-979a-e8ab25558421"]