Decoding an enum containing bools - ios

I have this JSON file.
[
{
"name": "January",
"holidays": [
{
"name": "New Year's Day",
"date": "2019-01-01T00:00:00-0500",
"type": {
"isNationalHoliday": true,
"isRegionalHoliday": true,
"isPublicHoliday": true,
"isGovernmentHoliday": true
}
},
{
"name": "Martin Luther King Day",
"date": "2019-01-21T00:00:00-0500",
"type": {
"isNationalHoliday": true,
"isRegionalHoliday": true,
"isPublicHoliday": true,
"isGovernmentHoliday": true
}
}
]
},
{
"name": "February",
"holidays": [
{
"name": "Presidents' Day",
"date": "2019-02-18T00:00:00-0500",
"type": {
"isNationalHoliday": false,
"isRegionalHoliday": true,
"isPublicHoliday": false,
"isGovernmentHoliday": false
}
}
]
},
{
"name": "March",
"holidays": null
}
]
I created a Month struct to decode the dictionaries in the JSON.
public struct Month {
public let name: String
public let holidays: [Holiday]?
}
extension Month: Decodable { }
And an Year struct to contain them all.
public struct Year {
public let months: [Month]
}
extension Year: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let values = try container.decode([Month].self)
months = values
}
}
My Holiday struct is a little more complex due to having an enum called HolidayType where I want to decode the values under the type field in the JSON.
public struct Holiday {
public let name: String
public let date: Date
public let type: HolidayType
}
extension Holiday: Decodable { }
public enum HolidayType {
case isNationalHoliday
case isRegionalHoliday
case isPublicHoliday
case isGovernmentHoliday
enum CodingKeys: String, CodingKey {
case isNationalHoliday
case isRegionalHoliday
case isPublicHoliday
case isGovernmentHoliday
}
}
extension HolidayType: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self = try container.decode(HolidayType.self, forKey: .isNationalHoliday)
self = try container.decode(HolidayType.self, forKey: .isRegionalHoliday)
self = try container.decode(HolidayType.self, forKey: .isPublicHoliday)
self = try container.decode(HolidayType.self, forKey: .isGovernmentHoliday)
}
}
This is where I'm loading the file and decoding.
if let url = Bundle.main.url(forResource: "holidays", withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let year = try decoder.decode(Year.self, from: data)
print(year.months)
} catch let error {
print("Error occurred decoding JSON: \(error)")
}
} else {
print("Error occurred loading file")
}
But it fails with the following error.
typeMismatch(Swift.Dictionary,
Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index
0", intValue: 0), CodingKeys(stringValue: "holidays", intValue: nil),
_JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "type", intValue: nil), CodingKeys(stringValue: "isNationalHoliday",
intValue: nil)], debugDescription: "Expected to decode
Dictionary but found a number instead.", underlyingError:
nil))
I can't figure out how to fix this. I also uploaded a demo project here.

You cannot use an enum to represent multiple booleans. If you want to keep your types without change, I would recommend using an OptionSet with custom decoding:
struct Year: Decodable {
let months: [Month]
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
months = try container.decode([Month].self)
}
}
struct Month: Decodable {
let name: String
let holidays: [Holiday]?
}
struct Holiday: Decodable {
let name: String
let date: Date
let type: HolidayType
}
struct HolidayType: OptionSet, Decodable {
let rawValue: Int
static let national = HolidayType(rawValue: 1 << 0)
static let regional = HolidayType(rawValue: 1 << 1)
static let `public` = HolidayType(rawValue: 1 << 2)
static let government = HolidayType(rawValue: 1 << 3)
init(rawValue: Int) {
self.rawValue = rawValue
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self = try CodingKeys.allCases
.filter { try container.decode(Bool.self, forKey: $0) }
.map { $0.type }
.reduce([] as HolidayType) { $0.union($1) }
}
private enum CodingKeys: String, CodingKey, CaseIterable {
case isNationalHoliday
case isRegionalHoliday
case isPublicHoliday
case isGovernmentHoliday
var type: HolidayType {
switch self {
case .isNationalHoliday:
return .national
case .isRegionalHoliday:
return .regional
case .isPublicHoliday:
return .public
case .isGovernmentHoliday:
return .government
}
}
}
}
or, instead of custom parsing, you can translate your types using a computed variable:
struct Holiday: Decodable {
let name: String
let date: Date
private let type: HolidayTypeHolder
var types: [HolidayType] {
return type.types
}
}
enum HolidayType: String {
case national, regional, `public`, `government`
}
private struct HolidayTypeHolder: Decodable {
let isNationalHoliday, isRegionalHoliday, isPublicHoliday, isGovernmentHoliday: Bool
var types: [HolidayType] {
var types: [HolidayType] = []
if isNationalHoliday {
types.append(.national)
}
if isRegionalHoliday {
types.append(.regional)
}
if isPublicHoliday {
types.append(.public)
}
if isGovernmentHoliday {
types.append(.government)
}
return types
}
}

"isNationalHoliday", intValue: nil)], debugDescription: "Expected to decode Dictionary but found a number instead.", underlyingError: nil))
isNationalHoliday is a bool value not an enum type , so on for isRegionalHoliday, isPublicHoliday, isGovernmentHoliday
You need
// MARK: - Element
struct Root: Codable {
let name: String
let holidays: [Holiday]?
}
// MARK: - Holiday
struct Holiday: Codable {
let name: String
let date: Date
let type: TypeClass
}
// MARK: - TypeClass
struct TypeClass: Codable {
let isNationalHoliday, isRegionalHoliday, isPublicHoliday, isGovernmentHoliday: Bool
}
let year = try decoder.decode([Root].self, from: data)

Related

Swift Key 'CodingKeys()' not found: No value associated with key CodingKeys() ("row")

"Key 'CodingKeys(stringValue: "row", intValue: nil)' not found: No
value associated with key CodingKeys(stringValue: "row", intValue:
nil) ("row"). codingPath: [CodingKeys(stringValue: "schoolInfo",
intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)] "
I am getting these errors
I'm doing API communication for school information
Here's the JSON response
{
"schoolInfo": [
{
"head": [
{
"list_total_count": 1
},
{
"RESULT": {
"CODE": "INFO-000",
"MESSAGE": "정상 처리되었습니다."
}
}
]
},
{
"row": [
{
"ATPT_OFCDC_SC_CODE": "F10",
"ATPT_OFCDC_SC_NM": "광주광역시교육청",
"SD_SCHUL_CODE": "7401173",
"SCHUL_NM": "정암초등학교",
"ENG_SCHUL_NM": "Jeong-Am Elementary School",
"SCHUL_KND_SC_NM": "초등학교",
"LCTN_SC_NM": "광주광역시",
"JU_ORG_NM": "광주광역시서부교육지원청",
"FOND_SC_NM": "공립",
"ORG_RDNZC": "62254 ",
"ORG_RDNMA": "광주광역시 광산구 첨단과기로 104",
"ORG_RDNDA": "(월계동)",
"ORG_TELNO": "062-970-4104",
"HMPG_ADRES": "http://jungam.gen.es.kr",
"COEDU_SC_NM": "남여공학",
"ORG_FAXNO": "062-972-3949",
"HS_SC_NM": null,
"INDST_SPECL_CCCCL_EXST_YN": "N",
"HS_GNRL_BUSNS_SC_NM": "일반계",
"SPCLY_PURPS_HS_ORD_NM": null,
"ENE_BFE_SEHF_SC_NM": "전기",
"DGHT_SC_NM": "주간",
"FOND_YMD": "19960301",
"FOAS_MEMRD": "19961002",
"LOAD_DTM": "20230115"
}
]
}
]
}
Here's the struct
struct schoolResponse: Codable {
let schoolInfo: [schoolRow]
}
struct schoolRow: Codable {
let row: [schoolsInfo]
}
struct schoolsInfo: Codable {
var schoolName: String
var schoolCode: String
var officeCode: String
private enum CodingKeys: String, CodingKey {
case schoolName = "SCHUL_NM"
case schoolCode = "SD_SCHUL_CODE"
case officeCode = "ATPT_OFCDC_SC_CODE"
}
}
Here's the function
protocol SchoolInfoProtocol: AnyObject {
var schoolData: PublishSubject<[schoolRow]> { get set }
}
class SchoolNameViewModel: BaseViewModel {
weak var delegate: SchoolInfoProtocol?
var schoolAddress: [schoolsInfo] = []
func fetchSchoolName(schoolName: String) {
let provider = MoyaProvider<SchoolNameAPI>()
provider.request(.schools(schoolName: schoolName, apiKey: "e6f3e10be1b1426cbcfb2be62afff409")) { (result) in
switch result {
case .success(let response):
let responseData = response.data
do {
let decoded = try JSONDecoder().decode(schoolResponse.self, from: responseData).schoolInfo
// self.delegate?.schoolData.onNext(decoded)
print(decoded)
} catch let DecodingError.dataCorrupted(context) {
print(context)
} catch let DecodingError.keyNotFound(key, context) {
print("Key '\(key)' not found:", context.debugDescription)
print("codingPath:", context.codingPath)
} catch let DecodingError.valueNotFound(value, context) {
print("Value '\(value)' not found:", context.debugDescription)
print("codingPath:", context.codingPath)
} catch let DecodingError.typeMismatch(type, context) {
print("Type '\(type)' mismatch:", context.debugDescription)
print("codingPath:", context.codingPath)
} catch {
print("error: ", error)
}
case .failure(let error):
print(error.localizedDescription)
print("1")
}
}
}
}
I want to solve this problem
I created a root model
Your structs don't match the JSON. Try this instead:
struct SchoolResponse: Codable {
let schoolInfo: [SchoolInfo]
}
struct SchoolInfo: Codable {
let head: [Head]?
let row: [Row]?
}
struct Head: Codable {
let listTotalCount: Int?
let result: Result?
enum CodingKeys: String, CodingKey {
case listTotalCount = "list_total_count"
case result = "RESULT"
}
}
struct Result: Codable {
let code, message: String
enum CodingKeys: String, CodingKey {
case code = "CODE"
case message = "MESSAGE"
}
}
struct Row: Codable {
let atptOfcdcScCode, atptOfcdcScNm, sdSchulCode, schulNm: String
let engSchulNm, schulKndScNm, lctnScNm, juOrgNm: String
let fondScNm, orgRdnzc, orgRdnma, orgRdnda: String
let orgTelno: String
let hmpgAdres: String
let coeduScNm, orgFaxno: String
let hsScNm: String?
let indstSpeclCccclExstYn, hsGnrlBusnsScNm: String
let spclyPurpsHsOrdNm: String?
let eneBfeSehfScNm, dghtScNm, fondYmd, foasMemrd: String
let loadDtm: String
enum CodingKeys: String, CodingKey {
case atptOfcdcScCode = "ATPT_OFCDC_SC_CODE"
case atptOfcdcScNm = "ATPT_OFCDC_SC_NM"
case sdSchulCode = "SD_SCHUL_CODE"
case schulNm = "SCHUL_NM"
case engSchulNm = "ENG_SCHUL_NM"
case schulKndScNm = "SCHUL_KND_SC_NM"
case lctnScNm = "LCTN_SC_NM"
case juOrgNm = "JU_ORG_NM"
case fondScNm = "FOND_SC_NM"
case orgRdnzc = "ORG_RDNZC"
case orgRdnma = "ORG_RDNMA"
case orgRdnda = "ORG_RDNDA"
case orgTelno = "ORG_TELNO"
case hmpgAdres = "HMPG_ADRES"
case coeduScNm = "COEDU_SC_NM"
case orgFaxno = "ORG_FAXNO"
case hsScNm = "HS_SC_NM"
case indstSpeclCccclExstYn = "INDST_SPECL_CCCCL_EXST_YN"
case hsGnrlBusnsScNm = "HS_GNRL_BUSNS_SC_NM"
case spclyPurpsHsOrdNm = "SPCLY_PURPS_HS_ORD_NM"
case eneBfeSehfScNm = "ENE_BFE_SEHF_SC_NM"
case dghtScNm = "DGHT_SC_NM"
case fondYmd = "FOND_YMD"
case foasMemrd = "FOAS_MEMRD"
case loadDtm = "LOAD_DTM"
}
}
(remove or rename properties as needed)

Parse JSON with different keys to same object using Codable in Swift

I receive the following 2 responses from different APIs
{
"id": "jdu72bdj",
"userInfo": {
"name": "Sudhanshu",
"age": 28,
"country": "India"
}
}
and
{
"profileId": "jdu72bdj",
"profileDetails": {
"name": "Sudhanshu",
"age": 28,
"country": "India"
}
}
This is in context with iOS development using Swift language.
Basically the object structure is same but keys are different. I'm parsing these using Codable, but I cannot think of a way to parse using same struct. All I can think of is making 2 different structs like this -
public struct Container1: Codable {
public let id: String
public let userInfo: UserProfile?
}
and
public struct Container2: Codable {
public let profileId: String
public let profileDetails: UserProfile?
}
They both use common UserProfile struct.
public struct UserProfile: Codable {
public let name: String?
public let age: Int?
public let country: String?
}
Is there a way to use one common container struct for both responses which parse response from 2 different keys. I do not want Container1 and Container2 since they both have same structure.
Any suggestions ?
One solution is to use a custom key decoding strategy using an implementation of CodingKey found in Apple's documentation. The idea is to map the keys of both of the json messages to the properties of the struct Container that will be used for both messages.
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom({ keys in
let key = keys.last!.stringValue
switch key {
case "id", "profileId":
return AnyKey(stringValue: "id")!
case "userInfo", "profileDetails":
return AnyKey(stringValue: "details")!
default:
return keys.last!
}
})
where the custom implementation of CodingKey is
struct AnyKey: CodingKey {
var stringValue: String
var intValue: Int?
init?(stringValue: String) {
print(stringValue)
self.stringValue = stringValue
intValue = nil
}
init?(intValue: Int) {
self.stringValue = String(intValue)
self.intValue = intValue
}
}
and then decode both json messages the same way using the below struct
struct Container: Codable {
let id: String
let details: UserProfile
}
let result = try decoder.decode(Container.self, from: data)
You can use your own init from decoder
struct UniversalProfileContainer: Decodable {
struct UserProfile: Decodable {
var name: String
var age: Int
var country: String
}
enum CodingKeys: String, CodingKey {
case id = "id"
case profileId = "profileId"
case userInfo = "userInfo"
case profileDetails = "profileDetails"
}
let id: String
let profile: UserProfile
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
id = try container.decode(String.self, forKey: .id)
} catch {
id = try container.decode(String.self, forKey: .profileId)
}
do {
profile = try container.decode(UserProfile.self, forKey: .userInfo)
} catch {
profile = try container.decode(UserProfile.self, forKey: .profileDetails)
}
}
}
let first = """
{
"id": "jdu72bdj",
"userInfo": {
"name": "Sudhanshu",
"age": 28,
"country": "India"
}
}
"""
let second = """
{
"profileId": "jdu72bdj",
"profileDetails": {
"name": "Sudhanshu",
"age": 28,
"country": "India"
}
}
"""
let response1 = try? JSONDecoder().decode(UniversalProfileContainer.self,
from: first.data(using: .utf8)!)
let response2 = try? JSONDecoder().decode(UniversalProfileContainer.self,
from: second.data(using: .utf8)!)

typeMismatch(Swift.Dictionary<Swift.String, Any> debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead."

Now I'm Facing this error
Value of type '[UserOrderHistory]' has no member 'orderlist'
My JSON :
{
"OrderList": [
{
"orderId": 16976,
"userId": 4905,
"pickupdate": "2018-09-23",
},
{
"orderId": 52,
"userId": 4905,
"pickupdate": "2018-08-07",
},
],
"TotalOrder": 2
}
My Decodable Model Class:
class UserOrderHistory: Object, Decodable {
var orderlist: [OrderList]?
var TotalO = RealmOptional<Int>()
enum CodingKeys: String, CodingKey {
case orderlist = "OrderList"
case TotalO = "TotalOrder"
}
convenience required init(from decoder: Decoder) throws {
self.init()
let container = try decoder.container(keyedBy: CodingKeys.self)
self.orderlist = try container.decodeIfPresent(OrderList.self, forKey: .orderlist)
self.TotalO.value = try container.decodeIfPresent(Int.self, forKey: .TotalO)
}
required init() {
super.init()
}
required init(value: Any, schema: RLMSchema) {
super.init(value: value, schema: schema)
}
required init(realm: RLMRealm, schema: RLMObjectSchema) {
super.init(realm: realm, schema: schema)
}
}
class OrderList: Object, Decodable{
var orderId = RealmOptional<Int>()
var userId = RealmOptional<Int>()
#objc dynamic var pickupdate: String? = nil
enum CatCodingKeys: String, CodingKey {
case orderId
case userId
case pickupdate
}
convenience required init(from decoder: Decoder) throws {
self.init()
let container = try decoder.container(keyedBy: CatCodingKeys.self)
self.orderId.value = try container.decodeIfPresent(Int.self, forKey: .orderId)
self.userId.value = try container.decodeIfPresent(Int.self, forKey: .userId)
self.pickupdate = try container.decodeIfPresent(String.self, forKey: .pickupdate)
}
required init() {
super.init()
}
required init(value: Any, schema: RLMSchema) {
super.init(value: value, schema: schema)
}
required init(realm: RLMRealm, schema: RLMObjectSchema) {
super.init(realm: realm, schema: schema)
}
}
Decode Server side Data to Realm Compatible:
let decoder = JSONDecoder()
do {
let orders = try decoder.decode(Array<UserOrderHistory>.self, from: data)
try? realm!.write {
realm?.add(orders.orderlist!)
}
}catch {
print(error)
}
Where is the problem.
How I can solve this.
Is there any other and easy way to parse this type of json to using decodable.
Have mention the error on top.
OrderList is an array. You need to change the variable orderList in UserOrderHistory class:
var orderlist: [OrderList]?
Keep this line as before only, remove Array:
let orders = try decoder.decode([UserOrderHistory].self, from: data)
to :
let orders = try decoder.decode(UserOrderHistory.self, from: data)
The following json object corresponds to only one instance of UserOrderHistory:
{
"OrderList": [
{
"orderId": 16976,
"userId": 4905,
"pickupdate": "2018-09-23",
},
{
"orderId": 52,
"userId": 4905,
"pickupdate": "2018-08-07",
},
],
"TotalOrder": 2
}
Hence using the following to decode will suffice:
let orders = try decoder.decode(UserOrderHistory.self, from: data)
You are trying to decode an array from an object the way I see it and that would fail.
Can you please try using Array<UserOrderHistory>.self instead of UserOrderHistory.self
In your UserOrderHistory class,
var orderlist: [OrderList]?
So the code should look like,
let decoder = JSONDecoder()
do {
let orders = try decoder.decode(Array<UserOrderHistory>.self, from: data)
try? realm!.write {
realm?.add(orders.orderlist!)
}
}catch {
print(error)
}

How to serialise JSON with dynamic types

I need to serialise a JSON response of some what below type. which has different type
[{
"type": "respiration_rate",
"value": 45
}
{ "type": "blood_pressure",
"value": { hg: 50 ,mm:120 }
}]
My class for serialising the upper json is
class Template: Codable {
var type: String?
var value: Double?
private enum CodingKeys: String, CodingKey {
case type
case value
}
}
How can serialise the value wether it be double or object dynamically?
Here is the code you need:
class Template: Codable {
let type: String?
let value: Value?
private enum CodingKeys: String, CodingKey {
case type
case value
}
typealias ValueDictionary = Dictionary<String, Int>
enum Value: Codable {
case double(Double)
case object(ValueDictionary)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode(Double.self) {
self = .double(x)
return
}
if let x = try? container.decode(ValueDictionary.self) {
self = .object(x)
return
}
throw DecodingError.typeMismatch(Value.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ValueUnion"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .double(let x):
try container.encode(x)
case .object(let x):
try container.encode(x)
}
}
}
}
You can use an enum with associated values instead of a class, as the JSON format seems be heterogenous.
enum Value: Decodable {
case respirationRate(Double)
case bloodPressure(hg: Double, mm: Double)
private struct BloodPresure: Decodable {
let hg: Double
let mm: Double
}
private enum CodingKeys: String, CodingKey {
case type
case value
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
switch try container.decode(String.self, forKey: .type) {
case "respiration_rate":
self = try .respirationRate(container.decode(Double.self, forKey: .value))
case "blood_pressure":
let details = try container.decode(BloodPresure.self, forKey: .value)
self = .bloodPressure(hg: details.hg, mm: details.mm)
case let type: throw DecodingError.dataCorruptedError(forKey: .value, in: container, debugDescription: "Invalid type: \(type)")
}
}
}
Decoding your json is now easy:
let json = """
[{
"type": "respiration_rate",
"value": 45
},
{ "type": "blood_pressure",
"value": { "hg": 50 ,"mm":120 }
}]
"""
do {
let values = try JSONDecoder().decode([Value].self, from: json.data(using: .utf8)!)
print(values)
} catch {
print(error)
}

Swift 4.1 Decodable Can't decode nested array with nestedContainer

Trying to write a simple Swift 4.1 using Codable to parse json.
I have a struct like this:
struct GameCharacter : Codable {
var name : String
var weapons : [Weapon]
enum CodingKeys : String, CodingKey {
case name
case weapons
}
init(from decoder: Decoder) {
do {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
let weaponsContainer = try container.nestedContainer(keyedBy: Weapon.CodingKeys.self, forKey: .weapons)
self.weapons = try weaponsContainer.decode([Weapon].self, forKey: .weapons)
} catch let error {
print("error: \(error)")
fatalError("error is \(error)")
}
}
}
and another like this:
struct Weapon : Codable {
var name : String
enum CodingKeys : String, CodingKey {
case name
}
init(from decoder: Decoder) {
do {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
} catch let error {
print("error: \(error)")
fatalError("error is \(error)")
}
}
}
I also have a struct for the wrapper like this:
struct Game : Codable {
var characters : [GameCharacter]
enum CodingKeys : String, CodingKey { case characters }
}
The json data looks like this:
{
"characters" : [{
"name" : "Steve",
"weapons" : [{
"name" : "toothpick"
}]
}]
}
However, I am always getting a typeMismatcherror error:
error: typeMismatch(Swift.Dictionary,
Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue:
"characters", intValue: nil), _JSONKey(stringValue: "Index 0",
intValue: 0)], debugDescription: "Expected to decode
Dictionary but found an array instead.", underlyingError:
nil))
on this line:
let weaponsContainer = try container.nestedContainer(keyedBy: Weapon.CodingKeys.self, forKey: .weapons)
I am not sure what the issue is, as I am clearly (in my view) asking for an array of Weapons, but it thinks I am looking for a dictionary anyway.
Wondering if anyone has any insight as to what I am missing.
nestedContainers is only needed if you want to decode a sub-dictionary or sub-array into the parent struct – for example decode the weapons object into the Game struct – which is not the case because you declared all nested structs.
To decode the JSON you can omit all CodingKeys and the initializers, take advantage of the magic of Codable, this is sufficient:
struct Game : Codable {
let characters : [GameCharacter]
}
struct GameCharacter : Codable {
let name : String
let weapons : [Weapon]
}
struct Weapon : Codable {
let name : String
}
and call it
do {
let result = try JSONDecoder().decode(Game.self, from: data)
print(result)
} catch { print(error) }
Replace your struct with following no need for any custom initializers
import Foundation
struct Weapon: Codable {
let characters: [Character]
}
struct Character: Codable {
let name: String
let weapons: [WeaponElement]
}
struct WeaponElement: Codable {
let name: String
}
And create
extension Weapon {
init(data: Data) throws {
self = try JSONDecoder().decode(Weapon.self, from: data)
}
Now just
let weapon = try Weapon(json)
try this
let string = """
{
"characters" : [{
"name" : "Steve",
"weapons" : [{
"name" : "toothpick"
}]
}]
}
"""
struct GameCharacter: Codable {
let characters: [Character]
}
struct Character: Codable {
let name: String
let weapons: [Weapon]
}
struct Weapon: Codable {
let name: String
}
let jsonData = string.data(using: .utf8)!
let decodr = JSONDecoder()
let result = try! decodr.decode(GameCharacter.self, from: jsonData)
let weapon = result.characters.flatMap {$0.weapons}
for weaponname in weapon {
print(weaponname.name) //Output toothpick
}
I Have the same problem, JSONDecoder() only decode the first level of my JSON and then I solve this problem with commented these methods from the body of my class that extended from Codable
public class Response<T:Codable> : Codable {
public let data : T?
//commented this two function and my problem Solved <3
// enum CodingKeys: String, CodingKey {
// case data
// }
// required public init(from decoder: Decoder) throws {
// data = try T(from: decoder)
// }
}

Resources