Can parse float from json to int via struct - json

I need to parse a json with a game instruction, but in the backend they change the param previously in Int to a Float (IDK why, but now is like that)
this is the error
Unexpected error: dataCorrupted(Swift.DecodingError.Context(codingPath: [ios_apps.HomeResponse.(CodingKeys in _B9BDF2CE76592A42518B3A0888B1E0F5).game, ios_apps.Game.(CodingKeys in _BAE470AF8C81659CF18E4A8D481877B9).counterLeft], debugDescription: "Parsed JSON number <53422.434> does not fit in Int.", underlyingError: nil)).
and this is the struct
import Foundation
struct Game : Decodable {
let gameId : String?
let currentTime : String?
let counterLeft : Int?
let counterStart : String?
let counterStep : String?
let gameEnd : String?
let gameIntroduction : String?
let gamePlay : Bool?
let gameStart : String?
let gameType : String?
let gameUserPlayed : Bool?
let picture : String?
let started : Bool?
// private var counterLeft : Float?
}
struct CounterLeft: Codable, Loopable {
let counterLeft : Int?
enum CodingKeys: String, CodingKey {
case counterLeft = "counterLeft"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
counterLeft = try Int(values.decode(Float.self, forKey: .counterLeft))
}
}
the response form json
game = {
counterLeft = "53422.434";
counterStart = "2018-03-26T07:00:00.000Z";
counterStep = countDown;
currentTime = "2018-03-26T21:09:37.562Z";
gameEnd = "2018-03-28T15:00:51.000Z";
gameId = 5ab906957889034b223b3ba4;
gameIntroduction = "Lorem Ipsum";
gamePlay = 0;
gameStart = "2018-03-27T12:00:00.000Z";
gameType = spinwheel;
gameUserPlayed = 0;
picture = "/files/1522075335538L.png";
started = 1;
};
I need the counterLeft to be in Int for countdown reasons

Use Double. I run a simple model of your test data, it is working.
Here is the code in swift 4:
var json = """
{
"counterLeft" : 53422.434,
"counterStart" : "2018-03-26T07:00:00.000Z"
}
""".data(using: .utf8)
struct model : Codable {
let counterLeft : Int?
let counterStart : String?
enum CodingKeys: String, CodingKey {
case counterLeft = "counterLeft"
case counterStart = "counterStart"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
counterLeft = try Int(values.decodeIfPresent(Double.self, forKey: .counterLeft) ?? 0)
counterStart = try values.decodeIfPresent(String.self, forKey: .counterStart)
}
}
let responseModel = try JSONDecoder().decode(model.self, from: json!)

Related

parse JSON and saving data to the realm is it possible?

struct NewsModel: Codable{
let id: Int?
let title, newsModelDescription: String?
let sourceID, version: String?
let publishedAt: Int
let readablePublishedAt: String?
let updatedAt: Int
let readableUpdatedAt: String
let images: Images
let embedTypes: String?
let typeAttributes: TypeAttributes
let type: String?
let source: String?
enum CodingKeys: String, CodingKey {
case id, title
case newsModelDescription
case sourceID
case version, publishedAt, readablePublishedAt, updatedAt, readableUpdatedAt, embedTypes, images,typeAttributes, type, source
}
}
// MARK: - Images
struct Images: Codable {
let square140: String
enum CodingKeys: String, CodingKey {
case square140 = "square_140"
}
}
struct TypeAttributes: Codable {
let imageLarge: String
}
This is my Model. I can successfully parse them and show them on UITableViewCell but I am unable to save them to the realm because these are struct. For save to realm I need to convert them to class and Realm object. But how I convert them to nested class. I want to use the same model to parse and saving data to the realm is it possible?
There are probably 100 different solutions. One option is to just make the object a Realm object that's conforms to the codable protocol. Something like this (not tested: so more of a conceptual solution)
class NewsModel: Object, Codable {
#objc dynamic var _id = UUID().uuidString
#objc dynamic var title = ""
#objc dynamic var news = ""
private enum CodingKeys: String, CodingKey {
case _id
case title
case news
}
override class func primaryKey() -> String? {
return "_id"
}
public required convenience init(from decoder: Decoder) throws {
self.init()
let container = try decoder.container(keyedBy: CodingKeys.self)
self._id = try container.decode(String.self, forKey: ._id)
self.name = try container.decode(String.self, forKey: .title)
self.logo = try container.decode(String.self, forKey: .news)
}
or change the model in the question to a class and add a function to save a realm object with the data. Again, not tested so this is just conceptual.
class RealmNewsModel: Object {
#objc dynamic var _id = ""
#objc dynamic var title = ""
#objc dynamic var news = ""
}
class NewsModel, Codable {
let _id: String?
let title: String?
let news: String?
func saveToRealm {
let news = RealmNewsModel()
news._id = self._id
news.title = self.title
news.news = self.news
try! realm.write {
realm.add(news)
}

I return a NIL using JSON Codable in Swift 4 - trying to return a value from a nested JSON example

I am trying to retrieve JSON data from a nested structure. I have shown a trimmed down version in 'jsonString' - I have copied in return results via print statements. what am I missing? all I want is the ItemCount number from this JSON data. Any help would be appreciated. Thank you!
let jsonString = """
{
"?xml": "Json Codeable- Swift",
"FHRSEstablishment": {
"Header": {
"#text":"",
"ExtractDate":"2019-08-10",
"ItemCount":"3789",
"ReturnCode":"Success",
"PageNumber":"1",
"PageSize":"100",
"PageCount":"38"
}
}
}
"""
let jsonDdata = Data(jsonString.utf8)
//rootStruct - 1
struct ratingFive : Codable {
let xml : String?
let fHRSEstablishment : FHRSEstablishment?
enum CodingKeys: String, CodingKey {
case xml = "?xml"
case fHRSEstablishment = "FHRSEstablishment"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
xml = try values.decodeIfPresent(String.self, forKey: .xml)
fHRSEstablishment = try FHRSEstablishment(from: decoder)
}
}
//Struct1 - 1.1
struct FHRSEstablishment : Codable {
let header : Header?
enum CodingKeys: String, CodingKey {
case header
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
header = try Header(from: decoder)
}
}
//Struct2 - 1.1.1
struct Header : Codable {
let text : String?
let extractDate : String?
let itemCount : String?
let pageCount : String?
let pageNumber : String?
let pageSize : String?
let returnCode : String?
enum CodingKeys: String, CodingKey {
case text = "#text"
case extractDate = "ExtractDate"
case itemCount = "ItemCount"
case pageCount = "PageCount"
case pageNumber = "PageNumber"
case pageSize = "PageSize"
case returnCode = "ReturnCode"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
text = try values.decodeIfPresent(String.self, forKey: .text)
extractDate = try values.decodeIfPresent(String.self, forKey: .extractDate)
itemCount = try values.decodeIfPresent(String.self, forKey: .itemCount)
pageCount = try values.decodeIfPresent(String.self, forKey: .pageCount)
pageNumber = try values.decodeIfPresent(String.self, forKey: .pageNumber)
pageSize = try values.decodeIfPresent(String.self, forKey: .pageSize)
returnCode = try values.decodeIfPresent(String.self, forKey: .returnCode)
}
}
do {
let ratingResult = try JSONDecoder().decode(ratingFive.self, from: jsonDdata)
print(ratingResult)// RETURNS:
//ratingFive(xml: Optional("Json Codeable- Swift"), fHRSEstablishment: Optional(__lldb_expr_1.FHRSEstablishment(header: Optional(__lldb_expr_1.Header(text: nil, extractDate: nil, itemCount: nil, pageCount: nil, pageNumber: nil, pageSize: nil, returnCode: nil)))))
print(ratingResult.xml) //RETURNS:
//Optional("Json Codeable- Swift")
print(ratingResult.fHRSEstablishment) //RETURNS:
//Optional(__lldb_expr_1.FHRSEstablishment(header: Optional(__lldb_expr_1.Header(text: nil, extractDate: nil, itemCount: nil, pageCount: nil, pageNumber: nil, pageSize: nil, returnCode: nil))))
print(ratingResult.fHRSEstablishment?.header?.itemCount) //RETURNS:
//NIL
}catch {
print(error)
}
I keep returning NIL when all I want is the ItemCount.
Thank you very much for your responses! I've opted for a slimmed down version and removed the init:
//rootStruct - 1
struct ratingFive: Codable {
let xml : String?
let fHRSEstablishment: FHRSEstablishment?
enum CodingKeys: String, CodingKey {
case xml = "?xml"
case fHRSEstablishment = "FHRSEstablishment"
}
}
//Struct1 - 1.1
struct FHRSEstablishment : Codable {
let Header : Header
}
//Struct2 - 1.1.1
struct Header : Codable {
let ItemCount : String?
}
do {
let ratingResult = try JSONDecoder().decode(ratingFive.self, from: jsonDdata)
print(ratingResult.fHRSEstablishment!.Header.ItemCount) //RETURNS: 3789!!!
}catch {
print(error)
}

how can I pass a json string to object?

my problem is the next, I have a this json
{
"nombre" : "userProfile.user.name!",
"apaterno" : 20,
"amaterno" : true,
"email" : 100,
"start_screen" : {
"info" : true,
"title" : false,
"image" : 20,
"success_btn" : "hola",
"close_btn" : true
}
}
i want to pass this json to my struct, my struct is :
struct person: Decodable
{
var email : Int
var nombre : String
var apaterno : Int
var amaterno: Bool
struct start_screen {
var title: Bool
var info: Bool
var image: Int
var success_btn: String
var close_btn: Bool
}
}
with the next lines I achieved put the json in my struct, but start_screen struct can't get the data.
let jsonData = json.data(using: .utf8)!
let decoder = JSONDecoder()
let myStruct = try! decoder.decode(person.self, from: jsonData)
when I access myStruct.email I get 100, its ok, but I can't load the start_screen data, how should I do it?
First you would need to add a variable to person for start_screen.
var start_screen: start_screen
Then you would need to make start_screen Decodable
struct start_screen: Decodable
That should be the minimum amount of changes to get it working.
Additionally, you might want to make your types capitalized. start_screen: start_screen is really confusing looking. You can also make your variable and type names camelCase and have the JSONDecoder convert to/from snake_case for you. It's also the naming convention in swift. It'd look like this
struct Person: Decodable {
var email: Int
var nombre: String
var apaterno: Int
var amaterno: Bool
var startScreen: StartScreen
struct StartScreen: Decodable {
var title: Bool
var info: Bool
var image: Int
var successBtn: String
var closeBtn: Bool
}
}
let jsonData = json.data(using: .utf8)!
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let person = try! decoder.decode(Person.self, from: jsonData)
print(person)
This should be your Person struct :
struct Person: Decodable {
var email : Int?
var nombre : String?
var apaterno : Int?
var amaterno: Bool
var start_screen: Start_screen?
enum CodingKeys: String, CodingKey {
case email = "email"
case nombre = "nombre"
case apaterno = "apaterno"
case amaterno = "amaterno"
case start_screen = "amaterno"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
email = try values.decodeIfPresent(Int.self, forKey: .email)
nombre = try values.decodeIfPresent(String.self, forKey: .nombre)
apaterno = try values.decodeIfPresent(Int.self, forKey: .apaterno)
amaterno = try values.decodeIfPresent(Bool.self, forKey: .apaterno) ?? false
start_screen = try values.decodeIfPresent(Start_screen.self, forKey: .start_screen)
}
}
This should be your Start_screen struct :
struct Start_screen: Decodable {
var title: Bool
var info: Bool
var image: Int?
var success_btn: String?
var close_btn: Bool
enum CodingKeys: String, CodingKey {
case title = "title"
case info = "info"
case image = "image"
case success_btn = "success_btn"
case close_btn = "close_btn"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
title = try values.decodeIfPresent(Bool.self, forKey: .title) ?? false
info = try values.decodeIfPresent(Bool.self, forKey: .info) ?? false
image = try values.decodeIfPresent(Int.self, forKey: .image)
success_btn = try values.decodeIfPresent(String.self, forKey: .success_btn)
close_btn = try values.decodeIfPresent(Bool.self, forKey: .close_btn) ?? false
}
}
Accessing start_screen from Person :
if let jsonData = json.data(using: .utf8) {
let user = try! JSONDecoder().decode(Person.self, from: jsonData)
if let title = user.start_screen.title {
print(title)
}
}

How to Add or Remove an object in result of JSON Codable Swift 4 (with dynamic Keys)

i want import JSON in swift with Codable, modify the object by adding or removing object, and export it in JSON.
Here, my structure
class GenericCodingKeys: CodingKey {
var stringValue: String
var intValue: Int?
required init?(stringValue: String) { self.stringValue = stringValue }
required init?(intValue: Int) { self.intValue = intValue; self.stringValue = "\(intValue)" }
}
class ListSpecie : Codable {
var species: [String : Specie]
required init(from decoder: Decoder) throws
{
let container = try decoder.container(keyedBy: GenericCodingKeys.self)
self.species = [String: Specie]()
for key in container.allKeys{
let value = try container.decodeIfPresent(Specie.self, forKey: GenericCodingKeys(stringValue: key.stringValue)!)
self.species[key.stringValue] = value
}
}
}
class Specie : Codable {
var name : String?
var latinName : [String]?
enum CodingKeys: String, CodingKey {
case name = "l"
case latinName = "ll"
}
required init(from decoder: Decoder) throws
{
let sValues = try decoder.container(keyedBy: CodingKeys.self)
name = try sValues.decodeIfPresent(String.self, forKey: .name)
latinName = try sValues.decodeIfPresent(Array<String>.self, forKey: .latinName)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(name, forKey: .name)
try container.encodeIfPresent(latinName, forKey: .latinName)
}
}
Here, the is a code with sample JSON
let myJson = """
{
"especeID1": {
"l": "Ail",
"ll": ["Allium sativum L.","Allium"]
},
"especeID2": {
"l": "Artichaut",
"ll": ["Cynara cardunculus"]
}
}
"""
let jsonDATA = myJson.data(using: .utf8)!
do{
self.jsonResult = try JSONDecoder().decode(ListSpecie.self, from: jsonDATA)
}catch{
print(error.localizedDescription)
}
Here, I want append or remove specie Object on jsonResult
for myspecie in (self.jsonResult?.species)! {
print(myspecie.key + " " + myspecie.value.name!)
}
// Encodage
let encoder = JSONEncoder()
let productJSON = try! encoder.encode(self.jsonResult?.species)
let jsonString = String(data: productJSON, encoding: .utf8)!
Someone could tell me how i can append or remove a specie object in my jsonResult variable .
Thanks a lot for the help you can bring me.
First of all your code is too complicated, most of the code is redundant.
One class (consider a struct) is sufficient
class Specie : Codable {
var name : String?
var latinName : [String]?
enum CodingKeys: String, CodingKey {
case name = "l"
case latinName = "ll"
}
}
If name and latin name is supposed to appear everywhere declare the properties non-optional (remove the question marks).
And decode the JSON
self.jsonResult = try JSONDecoder().decode([String:Specie].self, from: jsonDATA)
jsonResult is now a dictionary ([String:Specie]), you can remove items
self.jsonResult.removeValue(forKey: "especeID2")
or add an item
let newSpecies = Specie()
newSpecies.name = "Potato"
newSpecies.latinName = ["Solanum tuberosum"]
self.jsonResult["especeID3"] = newSpecies
and encode the object
let encoder = JSONEncoder()
let productJSON = try! encoder.encode(self.jsonResult)
let jsonString = String(data: productJSON, encoding: .utf8)!

What parsing object when property can be integer or bool?

Sometimes server sends me property as bool (true, false).
Sometimes server sends me property as an integer (0,1).
How can I decodable this case via standard Decodable in Swift 4?
Example.
I have:
final class MyOffer : Codable {
var id = 0
var pickupAsap: Int?
enum CodingKeys: String, CodingKey {
case id
case pickupAsap = "pickup_asap"
}
}
Responses from server are:
1) "pickup_all_day": true,
2) "pickup_all_day": 0
you may implement your own decode init method, get each class property from decode container, during this section, make your logic dealing with wether "asap" is an Int or Bool, sign all required class properties at last.
here is a simple demo i made:
class Demo: Decodable {
var id = 0
var pickupAsap: Int?
enum CodingKeys: String, CodingKey {
case id
case pickupAsap = "pickup_asap"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let id = try container.decode(Int.self, forKey: .id)
let pickupAsapBool = try? container.decode(Bool.self, forKey: .pickupAsap)
let pickupAsapInt = try? container.decode(Int.self, forKey: .pickupAsap)
self.pickupAsap = pickupAsapInt ?? (pickupAsapBool! ? 1 : 0)
self.id = id
}
}
mock data:
let jsonInt = """
{"id": 10,
"pickup_asap": 0
}
""".data(using: .utf8)!
let jsonBool = """
{"id": 10,
"pickup_asap": true
}
""".data(using: .utf8)!
test:
let jsonDecoder = JSONDecoder()
let result = try! jsonDecoder.decode(Demo.self, from: jsonInt)
print("asap with Int: \(result.pickupAsap)")
let result2 = try! jsonDecoder.decode(Demo.self, from: jsonBool)
print("asap with Bool: \(result2.pickupAsap)")
output:
asap with Int: Optional(0)
asap with Bool: Optional(1)
for more info: Apple's encoding and decoding doc