Swift 4 JSON Codable ids as keys

2020-02-13 05:33发布

I would like to use Swift 4's codable feature with json but some of the keys do not have a set name. Rather there is an array and they are ids like

{
 "status": "ok",
 "messages": {
   "generalMessages": [],
   "recordMessages": []
 },
 "foundRows": 2515989,
 "data": {
   "181": {
     "animalID": "181",
     "animalName": "Sophie",
     "animalBreed": "Domestic Short Hair / Domestic Short Hair / Mixed (short coat)",
     "animalGeneralAge": "Adult",
     "animalSex": "Female",
     "animalPrimaryBreed": "Domestic Short Hair",
     "animalUpdatedDate": "6/26/2015 2:00 PM",
     "animalOrgID": "12",
     "animalLocationDistance": ""

where you see the 181 ids. Does anyone know how to handle the 181 so I can specify it as a key? The number can be any number and is different for each one.

Would like something like this

struct messages: Codable {
    var generalMessages: [String]
    var recordMessages: [String]
}

struct data: Codable {
    var
}

struct Cat: Codable {
    var Status: String
    var messages: messages
    var foundRows: Int
    //var 181: [data] //What do I place here
}

Thanks in advance.

3条回答
三岁会撩人
2楼-- · 2020-02-13 05:43

I would suggest something like this:-

struct ResponseData: Codable {
struct AnimalData: Codable {
    var animalId: String
    var animalName: String

    private enum CodingKeys: String, CodingKey {
        case animalId = "animalID"
        case animalName = "animalName"
    }
}

var status: String
var foundRows: Int
var data: [AnimalData]

private enum CodingKeys: String, CodingKey {
    case status = "status"
    case foundRows = "foundRows"
    case data = "data"
}

struct AnimalIdKey: CodingKey {
    var stringValue: String
    init?(stringValue: String) {
        self.stringValue = stringValue
    }
    var intValue: Int? { return nil }
    init?(intValue: Int) { return nil }
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let animalsData = try container.nestedContainer(keyedBy: AnimalIdKey.self, forKey: .data)

    self.foundRows = try container.decode(Int.self, forKey: .foundRows)
    self.status = try container.decode(String.self, forKey: .status)
    self.data = []
    for key in animalsData.allKeys {
        print(key)
        let animalData = try animalsData.decode(AnimalData.self, forKey: key)
        self.data.append(animalData)
    }
  }
}

let string = "{\"status\":\"ok\",\"messages\":{\"generalMessages\":[],\"recordMessages\":[]},\"foundRows\":2515989,\"data\":{\"181\":{\"animalID\":\"181\",\"animalName\":\"Sophie\"}}}"
let jsonData = string.data(using: .utf8)!
let decoder = JSONDecoder()
let parsedData = try? decoder.decode(ResponseData.self, from: jsonData)

This way your decoder initializer itself handles the keys.

查看更多
手持菜刀,她持情操
3楼-- · 2020-02-13 05:53

Please check :

struct ResponseData: Codable {
    struct Inner : Codable {
        var animalID   : String
        var animalName : String

        private enum CodingKeys : String, CodingKey {
            case animalID     = "animalID"
            case animalName   = "animalName"
        }
    }

    var Status: String
    var foundRows: Int
    var data : [String: Inner]

    private enum CodingKeys: String, CodingKey {
        case Status = "status"
        case foundRows = "foundRows"
        case data = "data"
    }
}

let json = """
    {
        "status": "ok",
        "messages": {
            "generalMessages": ["dsfsdf"],
            "recordMessages": ["sdfsdf"]
        },
        "foundRows": 2515989,
        "data": {
            "181": {
                "animalID": "181",
                "animalName": "Sophie"
            },
            "182": {
                "animalID": "182",
                "animalName": "Sophie"
            }
        }
    }
"""
let data = json.data(using: .utf8)!
let decoder = JSONDecoder()

do {
    let jsonData = try decoder.decode(ResponseData.self, from: data)
    for (key, value) in jsonData.data {
        print(key)
        print(value.animalID)
        print(value.animalName)
    }
}
catch {
    print("error:\(error)")
}
查看更多
够拽才男人
4楼-- · 2020-02-13 05:54

I don't think you can declare a number as a variable name. From Apple's doc:

Constant and variable names can’t contain whitespace characters, mathematical symbols, arrows, private-use (or invalid) Unicode code points, or line- and box-drawing characters. Nor can they begin with a number, although numbers may be included elsewhere within the name.

A proper way is to have a property capturing your key.

struct Cat: Codable {
    var Status: String
    var messages: messages
    var foundRows: Int
    var key: Int // your key, e.g., 181
}
查看更多
登录 后发表回答