Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/14.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
如何使用带有动态键的Swift 4从嵌套JSON中提取数据_Json_Swift4 - Fatal编程技术网

如何使用带有动态键的Swift 4从嵌套JSON中提取数据

如何使用带有动态键的Swift 4从嵌套JSON中提取数据,json,swift4,Json,Swift4,我有一个JSON数据结构,使用上传时创建的唯一键。如果我逐行阅读字典中的每一项,我就能阅读所有的内容。然而,我正试图修改我的代码以使用Swift 4可编码属性 不幸的是,读这些书并没有把我推到天才的地位 JSON类似于以下简单示例: 请注意,诸如“123”、“456”、“case1”、“case2”、“u1”、“u2”等键在运行时是未知的 let json = """ { "things" : { "123" : { "name" : "Pa

我有一个JSON数据结构,使用上传时创建的唯一键。如果我逐行阅读字典中的每一项,我就能阅读所有的内容。然而,我正试图修改我的代码以使用Swift 4可编码属性

不幸的是,读这些书并没有把我推到天才的地位

JSON类似于以下简单示例: 请注意,诸如“123”、“456”、“case1”、“case2”、“u1”、“u2”等键在运行时是未知的

   let json = """
    {
    "things" : {
        "123" : {
        "name" : "Party",
        "owner" : "Bob",
        "isActive" : true,
        "cases" : {
            "case1" : {
                "no" : 1
            },
            "case2" : {
                "no" : 2
            }
        }
        },
        "456" : {
        "name" : "Bus",
        "owner" : "Joe",
        "isActive" : true
        }
    },
    "users" : {
        "u1" : {
        "name" : "Summer"
        },
        "u2" : {
        "name" : "Daffy"
        }
    }
    }
    """
接下来,我能够为大部分数据创建解码器,但不能为嵌套字典创建解码器(在本例中,cases的作用类似于嵌套字典)。我确信我遗漏了一些简单的东西

如果我试图包含注释掉的部分,操场将不会运行,不会给出错误

    struct Thing: Decodable {
    let id: String
    let isActive: Bool
    let name: String
    let owner: String
    //var cases = [Case]()

    init(id: String, isActive: Bool, name: String, owner: String){//}, cases: [Case]?) {
        self.id = id
        self.isActive = isActive
        self.name = name
        self.owner = owner
        //self.cases = cases ?? [Case(id: "none", caseNumber: 0)]
    }
}
struct User: Decodable {
    let id: String
    let name: String
}
struct Case: Decodable {
    let id: String
    let caseNumber: Int
}
struct ResponseData: Decodable {
    var things = [Thing]()
    var users = [User]()


    enum CodingKeys: String, CodingKey {
        case trips
        case users
    }

    private struct PhantomKeys: CodingKey {
        var intValue: Int?
        var stringValue: String
        init?(intValue: Int) { self.intValue = intValue; self.stringValue = "\(intValue)" }
        init?(stringValue: String) { self.stringValue = stringValue }
    }

    private enum ThingKeys: String, CodingKey {
        case isActive, name, owner, cases
    }
    private enum UserKeys: String, CodingKey {
        case name
    }
    private enum CaseKeys: String, CodingKey {
        case id
        case caseNumber = "no"
    }

    init(from decoder: Decoder) throws {

        let outer = try decoder.container(keyedBy: CodingKeys.self)
        let thingcontainer = try outer.nestedContainer(keyedBy: PhantomKeys.self, forKey: .things)

        for key in thingcontainer.allKeys {
            let aux = try thingcontainer.nestedContainer(keyedBy: ThingKeys.self, forKey: key)

            let name = try aux.decode(String.self, forKey: .name)
            let owner = try aux.decode(String.self, forKey: .owner)
            let isActive = try aux.decode(Bool.self, forKey: .isActive)
//            let c = try aux.nestedContainer(keyedBy: CaseKeys.self, forKey: .cases)
//            var cases = [Case]()
//            for ckey in c.allKeys {
//                let caseNumber = try c.decode(Int.self, forKey: .caseNumber)
//                let thiscase = Case(id: ckey.stringValue, caseNumber: caseNumber)
//                cases.append(thiscase)
//            }

            let thing = Thing(id: key.stringValue, isActive: isActive, name: name, owner: owner)//, cases: cases)
            things.append(thing)
        }
        let usercontainer = try outer.nestedContainer(keyedBy: PhantomKeys.self, forKey: .users)

        for key in usercontainer.allKeys {
            let aux = try usercontainer.nestedContainer(keyedBy: UserKeys.self, forKey: key)
            let name = try aux.decode(String.self, forKey: .name)
            let user = User(id: key.stringValue,name: name)
            users.append(user)
        }
    }
}
它适用于事物和用户,但我必须忽略这些情况。请参见注释//中的打印输出

let data = json.data(using: .utf8)!
let things = try JSONDecoder().decode(ResponseData.self, from: data).things
print(things[0])
//Thing(id: "456", isActive: true, name: "Bus", owner: "Joe")

let users = try JSONDecoder().decode(ResponseData.self, from: data).users
print(users[0])
//User(id: "u1", name: "Summer")
我曾尝试使用对我来说似乎更干净的指南,但我没有成功地实施它

这个代码是

我的问题有两个:

  • 如何在我的东西中以嵌套数组的形式获取案例数据
  • 你能吗 建议一种更简洁/更短的编码方式?感觉就像我是 重复这些内容,但我在中见过这种包装器结构 JSON编码/解码的几个示例

  • 您可以尝试以下方法:

    let data = jsonData.data(using: .utf8)
    let json = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
    let things = json["things"] as! [String:Any]
    
    for (thing_key, thing_value)   in things as [String:Any] {
    
        let thing = thing_value as! [String:Any]
        if let cases = thing["cases"] as? [String:Any]{
            for (case_key, case_value) in cases {
                print(case_key)
                print(case_value)
            }
        }
    }
    
    编辑 我最初不理解你的问题,这里是你的代码改进,以获得案件。这是一个快速的工作,所以可能不是最佳的,但你得到的想法:

    struct Thing: Decodable {
        let id: String
        let isActive: Bool
        let name: String
        let owner: String
        var cases: [Case]?
    
        init(id: String, isActive: Bool, name: String, owner: String , cases: [Case]?) {
            self.id = id
            self.isActive = isActive
            self.name = name
            self.owner = owner
            self.cases = cases
    
        }
    }
    struct User: Decodable {
        let id: String
        let name: String
    }
    struct Case: Decodable {
        let id: String
        let caseNumber: Int
    }
    struct ResponseData: Decodable {
        var things = [Thing]()
        var users = [User]()
    
    
        enum CodingKeys: String, CodingKey {
            case things
            case users
            case cases
        }
    
        private struct PhantomKeys: CodingKey {
            var intValue: Int?
            var stringValue: String
            init?(intValue: Int) { self.intValue = intValue; self.stringValue = "\(intValue)" }
            init?(stringValue: String) { self.stringValue = stringValue }
        }
    
        private enum ThingKeys: String, CodingKey {
            case isActive, name, owner, cases
        }
        private enum UserKeys: String, CodingKey {
            case name
        }
        private enum CaseKeys: String, CodingKey {
    
            case no
        }
    
        init(from decoder: Decoder) throws {
    
            let outer = try decoder.container(keyedBy: CodingKeys.self)
            let thingcontainer = try outer.nestedContainer(keyedBy: PhantomKeys.self, forKey: .things)
    
            for key in thingcontainer.allKeys {
                let aux = try thingcontainer.nestedContainer(keyedBy: ThingKeys.self, forKey: key)
    
                let name = try aux.decode(String.self, forKey: .name)
                let owner = try aux.decode(String.self, forKey: .owner)
                let isActive = try aux.decode(Bool.self, forKey: .isActive)
    
    
                var cases:[Case]? = []
                do{
                    let casescontainer = try aux.nestedContainer(keyedBy: PhantomKeys.self, forKey: .cases)
                        for case_key in casescontainer.allKeys{
    
                            let caseaux = try casescontainer.nestedContainer(keyedBy: CaseKeys.self, forKey: case_key)
                            let no = try caseaux.decode(Int.self, forKey: .no)
                            let thingCase = Case(id:case_key.stringValue, caseNumber: no)
                            cases?.append(thingCase)
                    }
    
                }catch{ }
    
    
                let thing = Thing(id: key.stringValue, isActive: isActive, name: name, owner: owner , cases: cases)
                things.append(thing)
            }
            let usercontainer = try outer.nestedContainer(keyedBy: PhantomKeys.self, forKey: .users)
    
            for key in usercontainer.allKeys {
                let aux = try usercontainer.nestedContainer(keyedBy: UserKeys.self, forKey: key)
                let name = try aux.decode(String.self, forKey: .name)
                let user = User(id: key.stringValue,name: name)
                users.append(user)
            }
        }
    }
    
    这将产生以下输出:

    let data = json.data(using: .utf8)!
    let things = try JSONDecoder().decode(ResponseData.self, from: data).things
    print("-----")
    for thing in things{
        print(thing)
    }
    print("---")
    let users = try JSONDecoder().decode(ResponseData.self, from: data).users
    for user in users{
        print(user)
    }
    


    您可以通过以下方式从当前json获取密钥:

     jq -r 'keys[]'
    

    在通过检索到的每个密钥在循环中进行查询之后

    您无法通过协议在Swift中自动编码/解码动态密钥的值–此类工作需要手动完成。我可以使用动态密钥执行此操作-见上文。“123”和“456”是动态键,我在获取数据时不知道。它通过“幻影键”工作——我想我是手动操作的。然而,我仍然无法理解嵌套的“案例”,我认为这是不可能的。请看我下面的答案,我重用了你的“幻影键”在你的“东西”中创建了一个可选的案例列表-我最初理解错了你的问题,因此编辑,我将原始响应留在那里只是为了参考或是为了百万次编辑,我花了一段时间才到达那里:)哦,天哪。当然,我需要再次使用幻影密钥,因为案例与对象和用户一样具有未知ID。谢谢你解决了这个问题@mike_t。你能在swift中使用
    jq
    吗?
     jq -r 'keys[]'