2

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.

2
  • The 181 is a string there (surrounded by ""). So you don't access element["data"][181] but rather element["data"]["181"] (note the double quotes). Is that what you're asking for? Commented Sep 21, 2017 at 0:23
  • Thanks for answering. I have updated my question to try to make myself clearer. Commented Sep 21, 2017 at 0:38

3 Answers 3

4

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)")
}
Sign up to request clarification or add additional context in comments.

Comments

0

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
}

2 Comments

You are right but I am trying to figure out what I need to place there.
Just simply use a variable to store your key
0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.