解析 Swift 中的动态可选键值 JSON

Parsing dynamic optional Key-Value JSON in Swift

如何解析动态键 (!papLZgcMalaVpYzStU:com.matrix.in":) 值或修改这两个结构以进行动态键处理

struct Join : Codable {

        let papLZgcMalaVpYzStUcommatrixin : !papLZgcMalaVpYzStU:com.matrix.in?

        enum CodingKeys: String, CodingKey {
                case papLZgcMalaVpYzStUcommatrixin = "!papLZgcMalaVpYzStU:com.matrix.in"
        }
    
        init(from decoder: Decoder) throws {
                let values = try decoder.container(keyedBy: CodingKeys.self)
                papLZgcMalaVpYzStUcommatrixin = !papLZgcMalaVpYzStU:com.matrix.in(from: decoder)
        }

}
struct !papLZgcMalaVpYzStU:com.matrix.in : Codable {

        let timeline : Timeline?

        enum CodingKeys: String, CodingKey {
                case timeline = "timeline"
        }
    
        init(from decoder: Decoder) throws {
                let values = try decoder.container(keyedBy: CodingKeys.self)
                timeline = Timeline(from: decoder)
        }

}

低于JSON.

{
"rooms": {
    "join": {
        "!papLZgcMalaVpYzStU:com.matrix.in": {
            "timeline": {
                "events": [{
                    "type": "m.room.message",
                    "sender": "@de1212138007:com.matrix.in",
                    "origin_server_ts": 1587395955315,
                    "content": {
                        "body": "thinks this is an example emote",
                        "msgtype": "m.emote"
                    },
                    "unsigned": {
                        "age": 7576335895
                    },
                    "event_id": "$$WLGTSEFSEF:localhost"
                }, {
                    "type": "m.room.message",
                    "sender": "@de3212138007:com.matrix.in",
                    "origin_server_ts": 1587395955315,
                    "content": {
                        "body": "filename.jpg",
                        "info": {
                            "h": 398,
                            "mimetype": "image/jpeg",
                            "size": 31037,
                            "w": 394
                        },
                        "msgtype": "m.image",
                        "url": "mxc://localhost/JWEIFJgwEIhweiWJE"
                    },
                    "unsigned": {
                        "age": 7576335895
                    },
                    "event_id": "$$WLGTSEFSEF:localhost"
                }, {
                    "type": "m.room.message",
                    "sender": "@de4212138007:com.matrix.in",
                    "origin_server_ts": 1587395955315,
                    "content": {
                        "body": "something-important.doc",
                        "filename": "something-important.doc",
                        "info": {
                            "mimetype": "application/msword",
                            "size": 46144
                        },
                        "msgtype": "m.file",
                        "url": "mxc://localhost/FHyPlCeYUSFFxlgbQYZmoEoe"
                    },
                    "unsigned": {
                        "age": 7576335895
                    },
                    "event_id": "$$WLGTSEFSEF:localhost"
                }],
                "prev_batch": "t16-12345_96395_1234_33138_4166_522_140_82600_1",
                "limited": true
            }


        },
        "!SecondRoomYzStU:com.matrix.in": {
            "prev_batch": "t16-98262_96395_3402_33138_4166_522_140_82600_1",
            "limited": true
        }
    }
}

}

我试过了

import Foundation

struct RootClass : Codable {

        let rooms : Room?

        enum CodingKeys: String, CodingKey {
                case rooms = "rooms"
        }
    
        init(from decoder: Decoder) throws {
            let values = try decoder.container(keyedBy: CodingKeys.self)
            rooms = try! Room(from: decoder)
        }

}
struct Room : Codable {

        let join : Join?

        enum CodingKeys: String, CodingKey {
                case join = "join"
        }
    
        init(from decoder: Decoder) throws {
                let values = try decoder.container(keyedBy: CodingKeys.self)
            join = try! Join(from: decoder)
        }

}

struct Join : Codable {

        let papLZgcMalaVpYzStUcommatrixin : !papLZgcMalaVpYzStU:com.matrix.in?

        enum CodingKeys: String, CodingKey {
                case papLZgcMalaVpYzStUcommatrixin = "!papLZgcMalaVpYzStU:com.matrix.in"
        }
    
        init(from decoder: Decoder) throws {
                let values = try decoder.container(keyedBy: CodingKeys.self)
                papLZgcMalaVpYzStUcommatrixin = !papLZgcMalaVpYzStU:com.matrix.in(from: decoder)
        }

}
struct !papLZgcMalaVpYzStU:com.matrix.in : Codable {

        let timeline : Timeline?

        enum CodingKeys: String, CodingKey {
                case timeline = "timeline"
        }
    
        init(from decoder: Decoder) throws {
                let values = try decoder.container(keyedBy: CodingKeys.self)
                timeline = Timeline(from: decoder)
        }

}

   struct Timeline : Codable {

                let events : [Event]?
                let limited : Bool?
                let prevBatch : String?

                enum CodingKeys: String, CodingKey {
                        case events = "events"
                        case limited = "limited"
                        case prevBatch = "prev_batch"
                }
            
                init(from decoder: Decoder) throws {
                        let values = try decoder.container(keyedBy: CodingKeys.self)
                        events = try values.decodeIfPresent([Event].self, forKey: .events)
                        limited = try values.decodeIfPresent(Bool.self, forKey: .limited)
                        prevBatch = try values.decodeIfPresent(String.self, forKey: .prevBatch)
                }

        }
struct Event : Codable {

        let content : Content?
        let eventId : String?
        let originServerTs : Int?
        let sender : String?
        let type : String?
        let unsigned : Unsigned?

        enum CodingKeys: String, CodingKey {
                case content = "content"
                case eventId = "event_id"
                case originServerTs = "origin_server_ts"
                case sender = "sender"
                case type = "type"
                case unsigned = "unsigned"
        }
    
        init(from decoder: Decoder) throws {
                let values = try decoder.container(keyedBy: CodingKeys.self)
            content = try! Content(from: decoder)
                eventId = try values.decodeIfPresent(String.self, forKey: .eventId)
                originServerTs = try values.decodeIfPresent(Int.self, forKey: .originServerTs)
                sender = try values.decodeIfPresent(String.self, forKey: .sender)
                type = try values.decodeIfPresent(String.self, forKey: .type)
            unsigned = try! Unsigned(from: decoder)
        }

}

struct Unsigned : Codable {

        let age : Int?

        enum CodingKeys: String, CodingKey {
                case age = "age"
        }
    
        init(from decoder: Decoder) throws {
                let values = try decoder.container(keyedBy: CodingKeys.self)
                age = try values.decodeIfPresent(Int.self, forKey: .age)
        }

}
struct Content : Codable {

        let body : String?
        let info : Info?
        let msgtype : String?
        let url : String?

        enum CodingKeys: String, CodingKey {
                case body = "body"
                case info = "info"
                case msgtype = "msgtype"
                case url = "url"
        }
    
        init(from decoder: Decoder) throws {
                let values = try decoder.container(keyedBy: CodingKeys.self)
                body = try values.decodeIfPresent(String.self, forKey: .body)
            info = try! Info(from: decoder)
                msgtype = try values.decodeIfPresent(String.self, forKey: .msgtype)
                url = try values.decodeIfPresent(String.self, forKey: .url)
        }

}
struct Info : Codable {

    let duration : Int?
    let mimetype : String?
    let size : Int?

    enum CodingKeys: String, CodingKey {
            case duration = "duration"
            case mimetype = "mimetype"
            case size = "size"
    }
}

您在 JSON 中有两种类型的 key-value

  1. 已知 key

  2. 未知 key

你应该用已知结构解析 known Key-Values,用简单的 unknown Key-Values Dictionary.

所以对于这个简单的 JSON:

{
   "a":{
      "RandomKey1":{
         "a":"Some required value",
         "b":"Some optional value here",
         "c":12345
      },
      "RandomKey2":{
         "a":"Another required value"
      }
   }
}

结构将是:

struct KnownTypeRandom: Codable {
    let a: String
    let b: String?
    let c: Int?
}

struct KnownTypeA: Codable {
    let a: [String: KnownTypeRandom]
}

然后您可以通过枚举未知键来找到未知键,例如:

for unknown in knownObjectA.a {
    print(unknown.key)
}

这将打印出:

RandomKey1
RandomKey2