How to create a Swift model for JSON - 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.

Related

Issues with Codable struct and Measuments<Unit>

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)

Codable with noisy JSON response [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
Generally, I understand how Codable works. A simple example would be:
[
{
"id": 1,
"name": "Paul",
"photo": {
"id": 48,
"url": "https://..."
}
},
{
"id": 2,
"name": "Andrew",
"photo": {
"id": 389,
"url": "https://..."
}
}
]
which would require two simple structs:
struct User: Codable {
var id: Int
var name: String
var photo: Photo
}
struct Photo: Codable {
var id: Int
var url: String
}
Given the example above, my API's response json comes in a more complicated format:
{
"data": [
{
"data": {
"id": 1,
"attributes": {
"name": "Paul"
},
"relationships": {
"photo": {
"data": {
"id": 48,
"attributes": {
"url": "https://..."
}
}
}
}
}
},
{
"data": {
"id": 2,
"attributes": {
"name": "Andrew"
},
"relationships": {
"photo": {
"data": {
"id": 389,
"attributes": {
"url": "https://..."
}
}
}
}
}
}
]
}
I've played around with SwiftyJSON to pre-parse / flatten the json by removing all the noise and trying to make the format like in the first json example. However, I feel like there's probably a better, more obvious way to do it, and manipulating the json seems very error prone.
I've read about CodingKeys, but at this point, that's almost as much extra work as it would be to parse the json all manually using SwiftyJSON.
Does anyone know how to do this efficiently?
Use quicktype.io to generate the response models for you from the specified JSON.
import Foundation
// MARK: - Root
struct Root: Codable {
let data: [Datum]
}
// MARK: - Datum
struct Datum: Codable {
let data: DatumData
}
// MARK: - DatumData
struct DatumData: Codable {
let id: Int
let attributes: PurpleAttributes
let relationships: Relationships
}
// MARK: - PurpleAttributes
struct PurpleAttributes: Codable {
let name: String
}
// MARK: - Relationships
struct Relationships: Codable {
let photo: Photo
}
// MARK: - Photo
struct Photo: Codable {
let data: PhotoData
}
// MARK: - PhotoData
struct PhotoData: Codable {
let id: Int
let attributes: FluffyAttributes
}
// MARK: - FluffyAttributes
struct FluffyAttributes: Codable {
let url: String
}
let root = try JSONDecoder().decode(Root.self, from: jsonData)

Parsing JSON in Swift without array key

I have a JSON-response:
[
[{
"id": "1",
"name": "Toyota",
"model": "Camry"
},
{
"id": "2",
"name": "Nissan",
"model": "Almera"
}
],
{
"count": "1234",
"page": "1"
}
]
I create decodable model:
struct Car: Decodable {
var id: String?
var name: String?
var model: String?
}
I'm trying extract data like this, for test:
let carsResponse = try JSONDecoder().decode([[Car]].self, from: data)
print(carsResponse[0])
And I have an error:
Expected to decode Array but found a dictionary instead.
What is wrong?
This format is pretty bad, so you'll need to decode the outer container by hand, but it's not difficult:
struct CarResponse: Decodable {
var cars: [Car]
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
cars = try container.decode([Car].self) // Decode just first element
}
}
let carsResponse = try JSONDecoder().decode(CarResponse.self, from: data)
print(carsResponse.cars)

Swift 4: how to implement codable protocol for json array of different types

Can somebody enlighten me how to implement codable protocol to swift structs, for the following json array of multiple types?
In the following json array of materials, it can be reflection object, video or note objects.
{
"materials": [
{
"reflection": {
"title": "3-2-1 reflection",
"description": "please reflect after today",
"questions": [
{
"question": "question 1",
"answer": "answer 1",
"answerImageUrl": "http://xxx"
},
{
"question": "question 1",
"answer": "answer 1",
"answerImageUrl": "http://xxx"
}
]
}
},
{
"video": {
"title": "the jungle",
"description": "please watch after today lesson",
"videoUrl": "http://"
}
},
{
"note": {
"title": "the note",
"description": "please read after today lesson"
}
}
]
}
If you can live with a Material that has three optional properties, this is quite straightforward:
struct Response: Codable {
let materials: [Material]
}
struct Material: Codable {
let reflection: Reflection?
let video: Video?
let note: Note?
}
struct Video: Codable {
let title, description, videoUrl: String
}
struct Reflection: Codable {
let title, description: String
let questions: [Question]
}
struct Question: Codable {
let question, answer, answerImageUrl: String
}
struct Note: Codable {
let title, description: String
}
let response = JSONDecoder().decode(Response.self, from: data)

Order of JSON node changes while making POST request Swift

I was trying to post the following jSON body
request JSON :
let parameters = [
"createTransactionRequest": [
"merchantAuthentication": [
"name": "xxxxxxxx",
"transactionKey": "xxxxxxxxx"
],
"refId": "123456",
"transactionRequest": [
"transactionType": "authCaptureTransaction",
"amount": "5",
"payment": [
"opaqueData": [
"dataDescriptor": desc!,
"dataValue": tocken!
]
]
]
]
]
When I am trying to print(parameters) the order of node changes it looks like
["createTransactionRequest":
["refId": "123456",
"transactionRequest":
["payment": ["opaqueData": ["dataDescriptor": "COMMON.ACCEPT.INAPP.PAYMENT", "dataValue": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx="]],
"transactionType": "authCaptureTransaction",
"amount": "5"],
"merchantAuthentication": ["name": "xxxxxxx", "transactionKey":
"6gvE46G5seZt563w"]
]
]
I am getting response like
{ messages = {
message = (
{
code = E00003;
text = "The element 'createTransactionRequest' in namespace
'AnetApi/xml/v1/schema/AnetApiSchema.xsd' has invalid child element
'refId' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'. List of
possible elements expected: 'merchantAuthentication' in namespace
'AnetApi/xml/v1/schema/AnetApiSchema.xsd'.";
}
);
resultCode = Error;
};
}
This is really annoying. anyones help will be highly grateful.
You need to change your data structure as follow:
struct Transaction: Codable {
let createTransactionRequest: CreateTransactionRequest
}
struct CreateTransactionRequest: Codable {
let merchantAuthentication: MerchantAuthentication
let refId: String
let transactionRequest: TransactionRequest
}
struct MerchantAuthentication: Codable {
let name: String
let transactionKey: String
}
struct TransactionRequest: Codable {
let transactionType: String
let amount: String
let payment: Payment
}
struct Payment: Codable {
let opaqueData: OpaqueData
}
struct OpaqueData: Codable {
let dataDescriptor: String
let dataValue: String
}
Testing
let json = """
{ "createTransactionRequest":
{ "merchantAuthentication":
{ "name": "YOUR_API_LOGIN_ID",
"transactionKey": "YOUR_TRANSACTION_KEY"
},
"refId": "123456",
"transactionRequest":
{ "transactionType": "authCaptureTransaction",
"amount": "5",
"payment":
{ "opaqueData":
{ "dataDescriptor": "COMMON.ACCEPT.INAPP.PAYMENT",
"dataValue": "PAYMENT_NONCE_GOES_HERE"
}
}
}
}
}
"""
let jsonData = Data(json.utf8)
do {
let transaction = try JSONDecoder().decode(Transaction.self, from: jsonData)
print(transaction)
// encoding
let encodedData = try JSONEncoder().encode(transaction)
print(String(data: encodedData, encoding: .utf8)!)
} catch {
print(error)
}
{"createTransactionRequest":{"merchantAuthentication":{"name":"YOUR_API_LOGIN_ID","transactionKey":"YOUR_TRANSACTION_KEY"},"refId":"123456","transactionRequest":{"transactionType":"authCaptureTransaction","amount":"5","payment":{"opaqueData":{"dataValue":"PAYMENT_NONCE_GOES_HERE","dataDescriptor":"COMMON.ACCEPT.INAPP.PAYMENT"}}}}}