使用通配符键解析JSON

使用通配符键解析JSON,json,swift,Json,Swift,我正在解析一个设计糟糕的JSON结构,在这个结构中,我可以期望找到被重用为指向进一步数据的键的值。像这样的 {"modificationDate" : "..." "type" : "...", "version" : 2, "manufacturer": "<WILDCARD-ID>" "<WILDCARD-ID>": { /* known structure */ } } 但这似乎很迂回,这让我觉得也许有一种更干净的方法来实现这一点?您可以使用可

我正在解析一个设计糟糕的JSON结构,在这个结构中,我可以期望找到被重用为指向进一步数据的键的值。像这样的

 {"modificationDate" : "..."
  "type" : "...",
  "version" : 2,
  "manufacturer": "<WILDCARD-ID>"

  "<WILDCARD-ID>": { /* known structure */ } }

但这似乎很迂回,这让我觉得也许有一种更干净的方法来实现这一点?

您可以使用可解码的自定义键,如下所示:

let json = """
    {
        "modificationDate" : "...",
        "type" : "...",
        "version" : 2,
        "manufacturer": "<WILDCARD-ID>",
        "<WILDCARD-ID>": {
            "foo": 1
        }
    }
    """.data(using: .utf8)!

struct InnerStruct: Decodable { // just as an example
    let foo: Int
}

struct Example: Decodable {
    let modificationDate: String
    let type: String
    let version: Int
    let manufacturer: String
    let innerData: [String: InnerStruct]

    enum CodingKeys: String, CodingKey {
        case modificationDate, type, version, manufacturer
    }

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

    init(from decoder: Decoder) throws {
        // extract all known properties
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.modificationDate = try container.decode(String.self, forKey: .modificationDate)
        self.type = try container.decode(String.self, forKey: .type)
        self.version = try container.decode(Int.self, forKey: .version)
        self.manufacturer = try container.decode(String.self, forKey: .manufacturer)

        // get the inner structs with the unknown key(s)
        var inner = [String: InnerStruct]()
        let customContainer = try decoder.container(keyedBy: CustomKey.self)
        for key in customContainer.allKeys {
            if let innerValue = try? customContainer.decode(InnerStruct.self, forKey: key) {
                inner[key.stringValue] = innerValue
            }
        }

        self.innerData = inner
    }
}

do {
    let example = try JSONDecoder().decode(Example.self, from: json)
    print(example)
}

您可以将自定义密钥与可解码密钥一起使用,如下所示:

let json = """
    {
        "modificationDate" : "...",
        "type" : "...",
        "version" : 2,
        "manufacturer": "<WILDCARD-ID>",
        "<WILDCARD-ID>": {
            "foo": 1
        }
    }
    """.data(using: .utf8)!

struct InnerStruct: Decodable { // just as an example
    let foo: Int
}

struct Example: Decodable {
    let modificationDate: String
    let type: String
    let version: Int
    let manufacturer: String
    let innerData: [String: InnerStruct]

    enum CodingKeys: String, CodingKey {
        case modificationDate, type, version, manufacturer
    }

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

    init(from decoder: Decoder) throws {
        // extract all known properties
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.modificationDate = try container.decode(String.self, forKey: .modificationDate)
        self.type = try container.decode(String.self, forKey: .type)
        self.version = try container.decode(Int.self, forKey: .version)
        self.manufacturer = try container.decode(String.self, forKey: .manufacturer)

        // get the inner structs with the unknown key(s)
        var inner = [String: InnerStruct]()
        let customContainer = try decoder.container(keyedBy: CustomKey.self)
        for key in customContainer.allKeys {
            if let innerValue = try? customContainer.decode(InnerStruct.self, forKey: key) {
                inner[key.stringValue] = innerValue
            }
        }

        self.innerData = inner
    }
}

do {
    let example = try JSONDecoder().decode(Example.self, from: json)
    print(example)
}

您可以捕获结构中特定但当前未知的键的概念:

struct StringKey: CodingKey {
    static let modificationDate = StringKey("modificationDate")
    static let type = StringKey("type")
    static let version = StringKey("version")
    static let manufacturer = StringKey("manufacturer")

    var stringValue: String
    var intValue: Int?
    init?(stringValue: String) { self.init(stringValue) }
    init?(intValue: Int) { return nil }
    init(_ stringValue: String) { self.stringValue = stringValue }
}
因此,解码非常简单,只解码与密钥匹配的结构:

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: StringKey.self)
    modificationDate = try container.decode(String.self, forKey: .modificationDate)
    type = try container.decode(String.self, forKey: .type)
    version = try container.decode(Int.self, forKey: .version)
    manufacturer = try container.decode(String.self, forKey: .manufacturer)

    // Decode the specific key that was identified by `manufacturer`,
    // and fail if it's missing
    manufacturerData = try container.decode(ManufacturerData.self,
                                            forKey: StringKey(manufacturer))
}

您可以捕获结构中特定但当前未知的键的概念:

struct StringKey: CodingKey {
    static let modificationDate = StringKey("modificationDate")
    static let type = StringKey("type")
    static let version = StringKey("version")
    static let manufacturer = StringKey("manufacturer")

    var stringValue: String
    var intValue: Int?
    init?(stringValue: String) { self.init(stringValue) }
    init?(intValue: Int) { return nil }
    init(_ stringValue: String) { self.stringValue = stringValue }
}
因此,解码非常简单,只解码与密钥匹配的结构:

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: StringKey.self)
    modificationDate = try container.decode(String.self, forKey: .modificationDate)
    type = try container.decode(String.self, forKey: .type)
    version = try container.decode(Int.self, forKey: .version)
    manufacturer = try container.decode(String.self, forKey: .manufacturer)

    // Decode the specific key that was identified by `manufacturer`,
    // and fail if it's missing
    manufacturerData = try container.decode(ManufacturerData.self,
                                            forKey: StringKey(manufacturer))
}
使用协议,对于通配符对象,您可以使用[String:]或将其解码为[String:Any],然后像您一样进行解析。对于通配符对象,您可以使用[String:]或将其解码为[String:Any],然后像您一样进行解析。