将响应转换为字典时,已解析响应的顺序已更改

Posted

技术标签:

【中文标题】将响应转换为字典时,已解析响应的顺序已更改【英文标题】:Order changed for the parsed response when casting the response into a dictionary 【发布时间】:2018-04-21 05:30:08 【问题描述】:

我从创建 jsonObject 的远程服务器收到响应。现在,当我迭代包含字典值的键“实例”的响应时,响应被打乱了。字典的关键元素是日期,在打印时没有按顺序出现。

这是 json 响应:


    "data": [
        
            "id": "apkqY19RthbN",
            "theater_id": "clzEVgztkcWB",
            "theater_audi_id": "7LFeNCaCrm8h",
            "movie_lang_id": "0VTEIjLeDxK1",
            "booking_start_date": null,
            "instances": 
                "2018-04-20": [
                    
                        "id": "WRK81ahlWcZZ",
                        "show_time": "17:00:00"
                    ,
                    
                        "id": "cDqZjYKHP2xt",
                        "show_time": "20:00:00"
                    
                ],
                "2018-04-21": [
                    
                        "id": "DcVQhohv9C3W",
                        "show_time": "17:00:00"
                    ,
                    
                        "id": "bZOUk3AT6TMM",
                        "show_time": "20:00:00"
                    
                ],
                "2018-04-22": [
                    
                        "id": "5YJAydTua6b2",
                        "show_time": "17:00:00"
                    ,
                    
                        "id": "qKGXgWvV0r38",
                        "show_time": "20:00:00"
                    
                ],
                "2018-04-23": [
                    
                        "id": "AGciEXINppMe",
                        "show_time": "17:00:00"
                    ,
                    
                        "id": "YbKFBJV67hFW",
                        "show_time": "20:00:00"
                    
                ],
                "2018-04-24": [
                    
                        "id": "5vj9t1i5B4J3",
                        "show_time": "17:00:00"
                    ,
                    
                        "id": "um7UeNPJuAcv",
                        "show_time": "20:00:00"
                    
                ],
                "2018-04-25": [
                    
                        "id": "AbS69J4SM4gG",
                        "show_time": "17:00:00"
                    ,
                    
                        "id": "gKax9RXMLozN",
                        "show_time": "20:00:00"
                    
                ]
            
        
    ]

这是我尝试过的代码:

 _ = URLSession.shared.dataTask(with: request)  (Data, response, error) in
            if Data != nil
                do

                    let access = try JSONSerialization.jsonObject(with: Data!, options: []) as! [String: Any]
                   // print(access)
                    if let data = access["data"] as? [[String:Any]]
                        if let id = data[0]["id"] as? String
                            global.showID = id
                          //  print(data)
                        
                        var j = 0
                        for l in 0..<data.count
                        if let instances = data[l]["instances"] as? [String:Any] 
                            print(instances)
                            for (key,obj) in instances
                                //for (key,obj) in (instances as [String:Any])
                                //print(key)
                                var showDetails: [ShowData] = [ShowData]()
                                if let timeObj = obj as? [[String: Any]]
                                    var k = 0
                                    for i in 0..<timeObj.count
                                        let showTime = timeObj[i]["show_time"]
                                        //print(showTime!)

                                        let showID = timeObj[i]["id"]
                                        //print(showID!)

                                        let a = ShowData.init(showId: showID as! String, showtime: showTime as! String)
                                        showDetails.insert(a, at: k)
                                        //print(showDetails)
                                        k += 1
                                    
                                
                                let a = Shows(date: key , instance: showDetails)
                                self.showsDate?.insert(a, at: j)
                                //print(self.showsDate!)
                                j += 1
                            
                            Completion(self.showsDate!)
                        
                        
                    
                catch let e
                    print(e)
                
            
            .resume()

print(instances) 当我打印这个输出是:

["2018-04-25": <__NSArrayI 0x143bd1ac0>(

    id = AbS69J4SM4gG;
    "show_time" = "17:00:00";
,

    id = gKax9RXMLozN;
    "show_time" = "20:00:00";

)
, "2018-04-24": <__NSArrayI 0x143b3aca0>(

    id = 5vj9t1i5B4J3;
    "show_time" = "17:00:00";
,

    id = um7UeNPJuAcv;
    "show_time" = "20:00:00";

)
, "2018-04-23": <__NSArrayI 0x143be0ce0>(

    id = AGciEXINppMe;
    "show_time" = "17:00:00";
,

    id = YbKFBJV67hFW;
    "show_time" = "20:00:00";

)
, "2018-04-21": <__NSArrayI 0x143b349c0>(

    id = DcVQhohv9C3W;
    "show_time" = "17:00:00";
,

    id = bZOUk3AT6TMM;
    "show_time" = "20:00:00";

)
, "2018-04-22": <__NSArrayI 0x143b24290>(

    id = 5YJAydTua6b2;
    "show_time" = "17:00:00";
,

    id = qKGXgWvV0r38;
    "show_time" = "20:00:00";

)
, "2018-04-20": <__NSArrayI 0x143bd5430>(

    id = WRK81ahlWcZZ;
    "show_time" = "17:00:00";
,

    id = cDqZjYKHP2xt;
    "show_time" = "20:00:00";

)
]

日期不像响应中那样按顺序排列。

【问题讨论】:

字典不保证键顺序。你可以考虑创建自己的OrderedDictionary 按顺序迭代响应的方法应该是什么。 嗯,这取决于。当我更新时,您可以寻找OrderedDictionary 的实现,或者您可以将键保存在Array 中,这将充当“排序”机制,然后您可以从Dictionary 中提取值通过Array 这很危险地接近于Swift - Stored values order is completely changed in Dictionary的副本 此外,从外观上看,它们的日期无论如何都是按日期顺序排列的,因此,您可以复制keys(到Array),所以Array 在您想要的方向并将其用作返回字典的向量 - timeObj[sortedKeys[0]] 或类似的东西 【参考方案1】:

Dictionary 不维护密钥顺序。

让我们从一些基本数据开始...

struct ShowData: CustomStringConvertible 
    var id: String
    var time: String
    
    var description: String 
        return "id = \(id); time = \(time)"
    


var rawData: [String: [ShowData]] = [:]

rawData["2018-04-20"] = [
    ShowData(id: "WRK81ahlWcZZ", time: "17:00:00"),
    ShowData(id: "cDqZjYKHP2xt", time: "20:00:00")]
rawData["2018-04-21"] = [
    ShowData(id: "DcVQhohv9C3W", time: "17:00:00"),
    ShowData(id: "bZOUk3AT6TMM", time: "20:00:00")]
rawData["2018-04-22"] = [
    ShowData(id: "5YJAydTua6b2", time: "17:00:00"),
    ShowData(id: "qKGXgWvV0r38", time: "20:00:00")]
rawData["2018-04-23"] = [
    ShowData(id: "AGciEXINppMe", time: "17:00:00"),
    ShowData(id: "YbKFBJV67hFW", time: "20:00:00")]
rawData["2018-04-24"] = [
    ShowData(id: "5vj9t1i5B4J3", time: "17:00:00"),
    ShowData(id: "um7UeNPJuAcv", time: "20:00:00")]
rawData["2018-04-25"] = [
    ShowData(id: "AbS69J4SM4gG", time: "17:00:00"),
    ShowData(id: "AbS69J4SM4gG", time: "20:00:00")]

for key in rawData.keys 
    print(key)

对我来说,这是打印出来的......

2018-04-25
2018-04-24
2018-04-23
2018-04-21
2018-04-20
2018-04-22

现在,您可以寻找OrderedDictionary 或者您可以Map 数组的键并对数组进行排序,这会将您的向量返回到Dictionary

或者,您可以对DictionaryEntry 数据进行排序...

let sorted = rawData.sorted  (lhs, rhs) -> Bool in
    return lhs.key < rhs.key


for entry in sorted 
    print(entry.key)

哪些输出...

2018-04-20
2018-04-21
2018-04-22
2018-04-23
2018-04-24
2018-04-25

现在,重要的是要记住,这只不过是一个String 比较,但您正在处理日期。就个人而言,我讨厌处理 String 日期/时间值,并且我会尽快转换为实际实用的东西...

let formatter = DateFormatter()
formatter.dateFormat = "yyyy-mm-dd"

let actuallySortedByDate = rawData.sorted  (lhs, rhs) -> Bool in
    return formatter.date(from: lhs.key)! < formatter.date(from: rhs.key)!


for entry in sorted 
    print(entry.key)

哪些输出...

2018-04-20
2018-04-21
2018-04-22
2018-04-23
2018-04-24
2018-04-25

虽然这看起来与第一次排序没有什么不同,但执行的比较对数据类型(日期对象)更为实际。

重要

这是一个想法的演示。了解密钥可能无效并且Date 转换可能会失败,这一点非常重要。就个人而言,我更喜欢rawDataDate 键入,而不是String,因为它可以更早地检测到问题。我在示例中使用“强制解包”纯粹是为了演示 - 您应该事先验证密钥并确保数据正确

警告: 映射Dictionary 中的键实际上比看起来需要的要困难得多。您可以查看What's the cleanest way of applying map() to a dictionary in Swift? 了解更多详情。不用多说,使用 Swift 4!

好的,所以它试图做的是将原始的 [String: ShowData] Dictionary 转换为 [Date: ShowData] Dictionary 以允许您处理无效的 String 密钥的可能性。

enum ParserError: Error 
    case invalidDateFormat


let formatter = DateFormatter()
formatter.dateFormat = "yyyy-mm-dd"
do 
    let mapped = try Dictionary(uniqueKeysWithValues: rawData.map( (entry) throws -> (Date, [ShowData]) in
        guard let date = formatter.date(from: entry.key) else 
            throw ParserError.invalidDateFormat
        
        return (Calendar(identifier: .gregorian).startOfDay(for: date), entry.value)
    ))

    let actuallySortedByDate = mapped.sorted  (lhs, rhs) -> Bool in
        return lhs.key < rhs.key
    
    
    for entry in actuallySortedByDate 
        print(entry.key)
    
 catch let error 
    print(error)

这最终会打印出来:

2018-01-19 13:00:00 +0000
2018-01-20 13:00:00 +0000
2018-01-21 13:00:00 +0000
2018-01-22 13:00:00 +0000
2018-01-23 13:00:00 +0000
2018-01-24 13:00:00 +0000

ps:我们的想法是尝试将时间分量排除在等式之外

【讨论】:

【参考方案2】:

字典是无序的。您还会发现处理变量键(您的日期)也可能很棘手。您应该将instances 设为一个数组并将日期放入字典值中。

如果您这样做,那么您可以创建几个采用 Codablestructs,并将您的 JSON 解析减少到 1 行

【讨论】:

以上是关于将响应转换为字典时,已解析响应的顺序已更改的主要内容,如果未能解决你的问题,请参考以下文章

如何将json字符串转换为字典并在键中保存顺序? [复制]

Ansible - 通过循环注册 GET 响应,多个相同的字典

wireshark对响应头发送顺序的误导

如何在“可编码”响应中使用字典?

如何将afnetworking json字典转换为字符串,然后在IOS中将字符串转换为字典?

将带有嵌套字典的json响应转换为pandas数据框[重复]