Issues with Codable struct and Measuments<Unit> - json

Can't figure out how to save to JSON standard units of the Dimension class, I have a struct:
struct Item: Hashable, Identifiable, Codable {
var id: Int
var name: String
var price: Int
var unit: Measurement<Unit>
}
Xcode doesn't throw any errors so I'm assuming Measurement can be encoded? I can't really make it working and save a json, and how should my json data look like if I want to load the struct with a test json?
[
{
"id": 1,
"name": "Test",
"price": 195,
"unit": ???
}
]
The idea is that I operate with the standard Dimension class that has all units I need (kg/g/L/ml) instead of creating my own class and describe all units from scratch.
Is it possible to have a JSON with "unit": "kg" that then will match a standard UnitMass.kilogram automatically?
Thanks.

For me this actually works with this (simplified) example:
struct Item: Codable {
var name: String
var unit: Measurement<Unit>
}
let json =
"""
{
"name": "Test",
"unit": {
"value": 12,
"unit": {
"symbol": "ml"
}
}
}
""".data(using: .utf8)!
let item = try JSONDecoder().decode(Item.self, from: json)
yields the correct result. You can replace the unit symbol with anything you like (kg, g, ml, e.t.c)

Related

How do I create a decodable struct to match JSON nest from API?

I am trying to create a decodable struct that will store select data from an API called MarketStack. The API uses JSON format and nests the information I am looking for like this.
{
"pagination": {
"limit": 100,
"offset": 0,
"count": 100,
"total": 9944
},
"data": [
{
"open": 129.8,
"high": 133.04,
"low": 129.47,
"close": 132.995,
"volume": 106686703.0,
"adj_high": 133.04,
"adj_low": 129.47,
"adj_close": 132.995,
"adj_open": 129.8,
"adj_volume": 106686703.0,
"split_factor": 1.0,
"dividend": 0.0,
"symbol": "AAPL",
"exchange": "XNAS",
"date": "2021-04-09T00:00:00+0000"
},
[...]
]
}
My struct currently looks like this.
struct ScrapeStruct: Decodable {
var high: Double
var low: Double
}
But I am worried that this won't access the right response objects because they are nested in "data". I thought I could maybe do something like this.
struct ScrapeStruct: Decodable {
var data: high
var data: low
}
Like I've seen online but still just get errors about it not following Decodable format. Any help or suggestions to research would be greatly appreciated. Thanks!
There are a couple of issues. Firstly, your JSON data is an array of objects and your struct is just an object. So, first create a struct to model each object. Also, using var in structs is not very useful considering structs have to be destroyed and recreated with a changed property to be modified (energy intensive process). It should look like this:
struct SingleData: Codable {
let open: Double
let high: Double
}
Now, ScrapeStruct should have a property called data that should be an array of SingleData. It should look like this:
struct ScrapeStruct: Decodable {
let data: [SingleData]
}
Now, as for your second struct. It will not work because "high" and "low" are types that do not exist. To parse (essentially converting languages, in this case JSON to Swift) you can only use basic types or objects created with basic types (such as Date, Int, String, Double, etc)
struct ScrapeStruct: Decodable {
var data: high //type high does not exist
var data: low //type low does not exist
}
If you want to avoid manual decoding and rely on synthesized Decodable implementation, your data structures should entirely match responses.
Your example is long, but for instance, for this shortened response of the similar structure:
{
"data" : [
{
"open" : 129.8,
"high" : 133.04
},
{
"open" : 123.4,
"high" : 567.8
}
]
}
This should work:
struct Response: Decodable {
struct Scrape: Decodable {
let open: Double
let high: Double
}
let data: [Scrape]
}

Type does not conform to Identifiable when decoding JSON data

Im trying to use a different json file for my app. The original json file has "id" in it but the new json file has "nr". So I changed my code to this:
import Foundation
struct Pokemon: Decodable, Identifiable {
let nr: Int
let name: String
let imageUrl: String
let type: String
}
Earlier code had let id: Int instead of let nr: Int.
After changing this code, I get the error:
Type pokemon does not conform to protocol identifiable
This was the old json file:
{
"attack": 49,
"defense": 49,
"description": "Bulbasaur can be seen napping in bright sunlight.\nThere is a seed on its back. By soaking up the sun’s rays,\nthe seed grows progressively larger.",
"evolutionChain": [
{
"id": "2",
"name": "ivysaur"
},
{
"id": "3",
"name": "venusaur"
}
],
"height": 7,
"id": 1,
"imageUrl": "https://firebasestorage.googleapis.com/v0/b/pokedex-bb36f.appspot.com/o/pokemon_images%2F2CF15848-AAF9-49C0-90E4-28DC78F60A78?alt=media&token=15ecd49b-89ff-46d6-be0f-1812c948e334",
"name": "bulbasaur",
"type": "poison",
"weight": 69
},
Whereas new file has "nr" instead of "id":
{
"nr": 1,
"nrp": "001",
"name": "Bulbasaur",
"class": "Seed Pok\u00e9mon",
"icon": "https:\/\/s6.postimg.org\/z17mvgtgx\/001.png",
"art": "https:\/\/s6.postimg.org\/bu799hi5t\/001.png",
"types": [
"Grass",
"Poison"
],
I found after searching that I need to use id as it is used as identifier. How to use that "nr" instead of id? Or any other way to use id in new json file?
You can use computable property, like
struct Pokemon: Decodable, Identifiable {
var id: Int { nr } // << here !!
let nr: Int
let name: String
let imageUrl: String
let type: String
}

How to define list of dictionaries in a struct

I want to load JSON that contains a list of dictionary, for example
{error: false, "objects": [{"id": 1, "name": "cat"}, {"id": 2, "name": "dog"}, {"id": 3, "name": "fish"}]
How could this be defined in a
struct name: Codable, Identifiable {
}
to be used in JSONDecoder to decode the received data.
How can the struct be written to define this datatype
struct data: Codable {
let error: Bool
let objects: [dataObjects]
}
struct dataObjects: Codable {
let id: Int
let name: String
}

How to create a Swift model for JSON

{"dataList":{"1547795650562": {
"c0a8007b-6759-111d-8167-59e8dabe0086": {
"recordDate": 1547795650562,
"resultValue": "160",
"vitalParameter": {
"uom": {
"code": "KG",
"name": "KG",
"id": "c0a8007b-6759-111d-8167-59e76204007f"
},
"resultType": {
"code": "VSRTNUMERIC",
"name": "Numeric",
"id": "20cf4756-40b0-4cc1-acb5-861765370a41"
},
"code": "29463-7",
"name": "Weight",
"id": "c0a8007b-6759-111d-8167-59e8dabe0086"
},
"id": "c0a8007b-6855-1d16-8168-5fd18fa301b7"
}}
}}
getting 1547795650562 and c0a8007b-6759-111d-8167-59e8dabe0086 as class names. But I dont want like this;
class DataList : NSObject, NSCoding{
var 1547795650562 : 1547795650562!
}
class 1547795650562 : NSObject, NSCoding{
var c0a8007b6759111d816759e8dabe0086 : VitalParameter!
}
But the problem here is, 1547795650562 and c0a8007b-6759-111d-8167-59e8dabe0086 cannot be hard coded because they may change.
c0a8007b-6759-111d-8167-59e8dabe0086 is dynamic id and 1547795650562 is recordDate. Inner object is repetitive.
But I have to map as the keys are of recordDate and id respectively.
Try using Codable instead of NSCoding to parse your JSON data.
Models:
struct Root: Codable {
let dataList: [String:[String:Record]]
}
struct Record: Codable {
let recordDate: Int
let resultValue: String
let vitalParameter: VitalParameter
let id: String
}
struct VitalParameter: Codable {
let uom, resultType: ResultType
let code, name, id: String
}
struct ResultType: Codable {
let code, name, id: String
}
Parse the JSON data using above models like,
do {
let response = try JSONDecoder().decode(Root.self, from: data)
print(response)
} catch {
print(error)
}
Note: You can use https://app.quicktype.io to get the models from your JSON instantly. Make the changes as per your requirement and you're good to go.

SWIFT 4 nested JSON Struct - Codable

I'm having issues creating a struct to parse JSON in Swift 4. I'm able to parse small JSONs and JSONDecoder seems to work fine. Just need help to create a struct to parse JSON like that:
{
"main": {
"solutions": [
{
"exersises": [
{
"book_title": "test",
"release_date": "2015-01-12T11:00",
"price": 100,
"additional": [
{
"item1": "test",
"item2": "test",
"number": 1
},
{
"item1": "test2",
"item2": "test2",
"number": 2
}
],
"availability": "Yes",
"item_id": 43534
}
]
}
]
}
}
What kind of struct do I need to get to value of book_title for example?
Its really easy. Your main probem is most likely root element. Let me get first layer or two for you.
let decoded = try JSONDecoder().decode(MainJSON.self, from: data)
class MainJSON: Codable {
var main:SolutionJSON?
}
class SolutionJSON: Codable {
var exercises:[ExercisesJSON]?
}
class ExercisesJSON: Codable {
var bookTitle: String?
var releaseDate: String?
var price: Double?
... etc
enum CodingKeys: String, CodingKey {
case bookTitle = "book_title"
case releaseDate = "release_date"
case price = "price"
}
}
ExerciseJSON also uses Codable interface which lets remap json properties into swift properties if they don't match. Hope this helps.
i prefer to give a general solution not only for this condition
it is very simple just download and run this MACOS APP from GITHUB
run it in your mac by XCODE and but your JSON in it,
it will make Models for any complex JSON
notes
1 if JSON keys have a capital character in the first it will be small
, so after copying model you need to change it like the JSON
2 if two JSON objects have the same structure and the same key names it will be only one model