I get the error:
Cannot initialize Role from invalid String value Mage
when I tried to interpret an array of string as enum type from a JSON file.
struct ChampionsData : Decodable{
let id : String
let key : String
let info : Info
let tags : [Role]
}
enum Role : String, CaseIterable, Decodable{
case Tank = "you believe that last person standing wins"
case Mage = "you like fantacies and tricking people"
case Assasin = "you enjoy living with danger"
case Fighter = "you are the warrior that built this town"
case Support = "you are a reliable teammate that always appears where you are needed "
case Marksman = "you tend to be the focus of the game, or the reason of victory or loss"
enum CodingKeys: String, CodingKey {
case mage = "Mage"
case assassin = "Assassin"
case tank = "Tank"
case fighter = "Fighter"
case support = "Support"
case marksman = "Marksman"
}
}
How can I parse it to a JSON object if I want to interpret tags as an array of Role enum type instead of an array of strings(or get rid of the error)?
Your JSON must be something like this
let jsonString = """
{
"id" :"asda",
"key" : "key asd",
"tags" : [
"Mage",
"Marksman"
]
}
"""
NOTE: I'm ignoring let info : Info here.
And from this string enum should be Mage, Marksman.. so on
But you have added them to be
case Mage = "you like fantacies and tricking people"*
In Enum raw values are implicitly assigned as CodingKeys
Update your code to this
enum Role : String, Decodable {
case tank = "Tank"
case mage = "Mage"
case assasin = "Assassin"
case fighter = "Fighter"
case support = "Support"
case marksman = "Marksman"
var value: String {
switch self {
case .tank:
return "you believe that last person standing wins"
case .mage:
return "you like fantacies and tricking people"
case .assasin:
return "you enjoy living with danger"
case .fighter:
return "you are the warrior that built this town"
case .support:
return "you are a reliable teammate that always appears where you are needed"
case .marksman:
return "you tend to be the focus of the game, or the reason of victory or loss"
}
}
}
Then you can just use the value after decoding like this
let data = jsonString.data(using: .utf8)!
// Initializes a Response object from the JSON data at the top.
let myResponse = try! JSONDecoder().decode(ChampionsData.self, from: data)
print(myResponse.tags.first?.value as Any)
If we used the json mentioned in the start we will get
"you like fantacies and tricking people"
Related
Reading in from small JSON file (cut/pasted small sample) and bring it into structs. I am using CodingKeys to change the "string key" to match what I want in the struct. I am changing the "whoresponds" key to "respondingPilot". That is all working fine. However, I also want to look at the values on that property and change on-the-fly the value brought in. For example, if I get the string "BOTH", I want to change my data that is stored to "CAPT & F/O". Same if I see "FO", I want that changed to "F/O" as it read in. Sample below reads in fine but will not make the changes. Still learning but must be something simple I am missing. Thanks!
struct CheckListsJSON: Codable {
var name: String
var checklistItems: [ChecklistItemsJSON]
}
struct ChecklistItemsJSON: Codable, Identifiable {
var challenge: String
var respondingPilot: PilotResponding
let id = UUID()
private enum CodingKeys: String, CodingKey {
case challenge
case respondingPilot = "whoresponds"
}
enum PilotResponding: String, Codable {
case CPT = "CAPT"
case FO = "F/O"
case PF = "PF"
case PM = "PM"
case BOTH = "CAPT & F/O"
}
}
let jsonAC = "{\"name\": \"After Start\", \"checklistItems\": [{\"challenge\": \"Generators\", \"whoresponds\": \"CPT\"}, {\"challenge\": \"Isolation Valve\", \"whoresponds\": \"FO\"}}"
let fileData = Data(jsonAC.utf8)
do {
let decodedData = try JSONDecoder().decode(CheckListsJSON.self, from: fileData)
print("decoded:", decodedData)
} catch {
print(error)
}
Decoding a string to an enum works like CodingKeys. The raw value is the string you receive and the case is the case.
Unfortunately the character set of enum cases is restricted. Space characters, / and & are not allowed.
You could write something like this, raw values which match the case can be omitted.
enum PilotResponding: String, Codable {
case CAPT = "CPT"
case F_O = "FO"
case PF, PM
case CAPT_F_O = "BOTH"
}
I am trying to convert time series data received from the website alpha vantage to Swift object however the error message shown below prevents it from happening. Also my network function works. How do I resolve this ?
https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY_ADJUSTED&symbol=IBM&apikey=demo
keyNotFound(CodingKeys(stringValue: "meta", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"meta\", intValue: nil) (\"meta\").", underlyingError: nil))
struct TimeSeriesMonthlyAdjusted :Decodable{
let meta: Meta
let timeSeries: [String:OHLC]
}
struct Meta :Decodable{
let symbol:String
enum CodingKey:String{
case symbol = "2. Symbol"
}
}
struct OHLC : Decodable{
let open :String
let close : String
let adjustedClose: String
enum CodingKey:String{
case open = "1. open"
case close = "4. close"
case adjustedClose = "5. adjusted close"
}
}
So two things (which worked for me). Adding CodingKey support TimeSeriesMonthlyAdjusted
struct TimeSeriesMonthlyAdjusted :Decodable{
enum CodingKeys:String, CodingKey {
case meta = "Meta Data"
case timeSeries = "Monthly Adjusted Time Series"
}
let meta: Meta
let timeSeries: [String:OHLC]
}
and changing enum CodingKey: String {...} to enum CodingKeys: String, CodingKey {...} for all your structs
Playground test
Make sure you add Test.json to the Resources folder in the Playground
struct TimeSeriesMonthlyAdjusted :Decodable{
enum CodingKeys:String, CodingKey {
case meta = "Meta Data"
case timeSeries = "Monthly Adjusted Time Series"
}
let meta: Meta
let timeSeries: [String:OHLC]
}
struct Meta :Decodable{
let symbol:String
enum CodingKeys:String, CodingKey{
case symbol = "2. Symbol"
}
}
struct OHLC : Decodable{
let open :String
let close : String
let adjustedClose: String
enum CodingKeys:String, CodingKey {
case open = "1. open"
case close = "4. close"
case adjustedClose = "5. adjusted close"
}
}
do {
let data = try Data(contentsOf: Bundle.main.url(forResource: "Test", withExtension: "json")!)
try JSONDecoder().decode(TimeSeriesMonthlyAdjusted.self, from: data)
} catch let error {
error
}
You might also want to make use of https://app.quicktype.io for quickly converting JSON to Swift
I'm having trouble parsing a response into workable objects in swift.
Basically, this is the response I get (simplified data for viewing purposes)
"[{\"CO2\":0,\"Places\":[{\"Name\":\"RT(Esc. Sec.)\",\"Code\":\"ST_RT\",\"Type\":0,\"CoordX\":41.176750183105469,\"CoordY\":-8.5490522384643555,\"Provider\":\"ST\",\"Lines\":null},{\"Name\":\"Esc.Sec RT\",\"Code\":\"ST_RT2\",\"Type\":0,\"CoordX\":41.175251007080078,\"CoordY\":-8.54929256439209,\"Provider\":\"ST\",\"Lines\":null},{\"Name\":\"SM\",\"Code\":\"ST_SM\",\"Type\":0,\"CoordX\":41.173740386962891,\"CoordY\":-8.5474367141723633,\"Provider\":\"ST\",\"Lines\":null}],\"Direction\":\"R\"}]"
After I receive the response I do the following:
let dict = try! JSONSerialization.jsonObject(data: responseData!, options: .allowFragments) as? [[String:Any]] ?? [[String:Any]]()
Which results in the following dictionary (I'm sorry for the picture, but I could not take a print screen. Plus those 44 elements are the total number of Places, but I've simplified the response string as I said above)
My problem is, I cannot access each Place dictionary. I've tried iterating through dict["Places"] but that does not seem to work, which I do not understand, given it is an NSArray.
I may be missing something simple, but I can't seem to figure it out.
Any help is welcome.
Thanks!
You can try
// MARK: - Root
struct Root: Codable {
let co2: Int
let places: [Place]
let direction: String
enum CodingKeys: String, CodingKey {
case co2 = "CO2"
case places = "Places"
case direction = "Direction"
}
}
// MARK: - Place
struct Place: Codable {
let name, code: String
let type: Int
let coordX, coordY: Double
let provider: String
let lines: String?
enum CodingKeys: String, CodingKey {
case name = "Name"
case code = "Code"
case type = "Type"
case coordX = "CoordX"
case coordY = "CoordY"
case provider = "Provider"
case lines = "Lines"
}
}
let res = try JSONDecoder().decode([Root].self,from:data)
print(res.places)
I am getting some json like so (this is pseudo, not all keys are here):
{
"absolute_magnitude_h" = "23.4";
...
"close_approach_data" = (
{
"close_approach_date" = "1994-09-03";
"epoch_date_close_approach" = 778575600000;
"orbiting_body" = Earth;
}
I have an object with this struct:
struct NEOObj:Codable {
var absoluteMagnitudeH:Float
var designation:String
var isPotentiallyHazardousAsteroid:Bool
var isSentryObject:Bool
var name:String
var nasaJPLURL:String
var neoReferenceID:String
var closeApproachData:[NEOCloseApproachData] = [NEOCloseApproachData]()
private enum CodingKeys: String, CodingKey {
case absoluteMagnitudeH = "absolute_magnitude_h"
case designation = "designation"
case isPotentiallyHazardousAsteroid = "is_potentially_hazardous_asteroid"
case isSentryObject = "is_sentry_object"
case name = "name"
case nasaJPLURL = "nasa_jpl_url"
case neoReferenceID = "neo_reference_id"
}
enum CloseApproachCodingKeys: String, CodingKey {
case closeApproachDate = "close_approach_date"
case epochDateCloseApproach = "epoch_date_close_approach"
case orbitingBody = "orbiting_body"
}
struct NEOCloseApproachData:Codable {
var closeApproachDate:Date
var epochDateCloseApproach:Date
var orbitingBody:String
enum CodingKeys: String, CodingKey {
case closeApproachDate = "close_approach_date"
case epochDateCloseApproach = "epoch_date_close_approach"
case orbitingBody = "orbiting_body"
}
}
and in my file I have this code:
if let arrNEOs = dictJSON["near_earth_objects"] as? Array<Any> {
for thisNEODict in arrNEOs {
do {
let jsonData = try JSONSerialization.data(withJSONObject: thisNEODict, options: .prettyPrinted)
let thisNEOObj = try? JSONDecoder().decode(NEOObj.self, from: jsonData)
print(thisNEOObj!.closeApproachData)
} catch {
}
}
}
But closeApproachData never gets populated. What am I doing wrong?
There are multiple things that would need to be changed for it to work.
closeApproachData should probably be of type [NEOCloseApproachData]. You haven't included CloseApproachCodingKeys in your code but it probably isn't right.
NEOObj.CodingKeys needs to have a case for closeApproachData which would look like
case closeApproachData = "close_approach_data"
NEOCloseApproachData needs a custom decoder to work with the dates for closeApproachDate and epochDateCloseApproach. Since the JSON has different formats for each (String and Int respectively), you can't use on the JSONDecoder.dateDecodingStrategy since it will apply to all dates.
The reason why after decoding you get closeApproachData property empty is the lack of the regarding key for that property into your CodingKeys enum.
Regarding to the Apple doc, it tells next:
Omit properties from the CodingKeys enumeration if they won't be
present when decoding instances, or if certain properties shouldn't be
included in an encoded representation.
That means that generated implementation of decode method for your NEOObj class omit decoding implementation for the closeApproachData property.
Here's a link at the Apple documentation about Encoding and Decoding Custom Types. You can find more details at the "Choose Properties to Encode and Decode Using Coding Keys" paragraph.
Also your custom class NEOCloseApproachData should be conformed to the Coding protocol.
I am using enum and tuple with value of enum case. I've trouble with getting value from [String: String] constant.
I don't know how to fix it, it has to be a trap, but I don't know where, as the key is surely string:
enum DictTypes : String {
case settings
case options
case locations
}
enum FileTypes : String {
case json
case pList
}
func getCodebookUrlComponent() -> String
{
var FileSpecs: (
dictType: DictTypes,
fileType: FileTypes,
redownload: Bool
) = (.settings, .json, true)
let codebooks = [
"settings" : "settings",
"options" : "options"
]
let key = self.FileSpecs.dictType // settings or options
if let urlComponent = codebooks[key] {
return urlComponent
}
return ""
}
This line if let urlComponent = codebooks[key] comes with an error:
Ambiguous reference to member 'subscript'
You should use .rawValue for this case :
if let urlComponent = codebooks[key.rawValue]{
return urlComponent
}
This problem occurs because of the let key = self.FileSpecs.dictType in this line you receive key that is FileSpecs type. And subscript that is implemented in the Array will not conform for that value type.
rawValue in you're case return String value that you assign in you're enum.
Since value from enum case is surely string, I would type it like this:
let key = FileSpecs.dictType.rawValue // "settings" or "options"
or
let key = String(describing: FileSpecs.dictType)
return codebooks[key]!