Codable with noisy JSON response [closed] - json

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)

Related

Swift: Decode 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
I have a backend response that has the format:
{
"Items": [
{
"Id": "blabla",
"Text": "blabla",
"Description": "blabla"
},
{
"Id": "blabla",
"Text": "blabla",
"Description": "blabla"
}]
}
Which will be the best Swift approach to directly decode the array?
For the moment I have a struct for the response, but I have also to take in account the "Items" key, which hasn't any business logic implication for the project.
struct SearchResult: Decodable {
let Id: String
let Text: String
let Description: String
}
Your JSON is missing a trailing ] on the end of the array.
Besides that, you just need a wrapper than gives you the Items array. This, for example, works fine:
let jsonData = """
{
"Items": [
{
"Id": "blabla",
"Text": "blabla",
"Description": "blabla"
},
{
"Id": "blabla",
"Text": "blabla",
"Description": "blabla"
}
]
}
""".data(using: .utf8)!
struct ResultWrapper: Decodable {
var Items : [SearchResult]
}
struct SearchResult: Decodable {
let Id: String
let Text: String
let Description: String
}
do {
let results = try JSONDecoder().decode(ResultWrapper.self, from: jsonData)
print(results.Items) //this is your array
} catch {
print(error)
}

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.

How do you parse recursive JSON in Swift?

I am receiving JSON from a server where the data is recursive. What is the best way to parse this into a convenient Swift data structure?
Defining a Swift Codable data structure to parse it into fails because the recursive properties are not allowed.
The Swift compiler reports: "Value type 'FamilyTree.Person' cannot have a stored property that recursively contains it"
{
"familyTree": {
"rootPerson": {
"name": "Tom",
"parents": {
"mother": {
"name": "Ma",
"parents": {
"mother": {
"name": "GraMa",
"parents": {}
},
"father": {
"name": "GraPa",
"parents": {}
}
}
},
"father": {
"name": "Pa",
"parents": {}
}
}
}
}
}
Ideally the end result would be a bunch of person objects pointing to their mother and father objects starting from a rootPerson object.
The first thought is to create structs such as:
struct Person: Codable {
var name: String
var parents: Parents
}
struct Parents: Codable {
var mother: Person?
var father: Person?
}
But this doesn't work since you can't have recursive stored properties like this.
Here is one possible working solution:
let json = """
{
"familyTree": {
"rootPerson": {
"name": "Tom",
"parents": {
"mother": {
"name": "Ma",
"parents": {
"mother": {
"name": "GraMa",
"parents": {}
},
"father": {
"name": "GraPa",
"parents": {}
}
}
},
"father": {
"name": "Pa",
"parents": {}
}
}
}
}
}
"""
struct Person: Codable {
var name: String
var parents: [String: Person]
}
struct FamilyTree: Codable {
var rootPerson: Person
}
struct Root: Codable {
var familyTree: FamilyTree
}
let decoder = JSONDecoder()
let tree = try decoder.decode(Root.self, from: json.data(using: .utf8)!)
print(tree)
In a playground this will correctly parse the JSON.
The parents dictionary of Person will have keys such as "mother" and "father". This supports a person have any number of parents with any role.
Possible implementation using classes.
(Swift 5 synthesizes default initializers for classes. Dont remember if so for Swift 4)
import Foundation
var json: String = """
{
"familyTree": {
"rootPerson": {
"name": "Tom",
"parents": {
"mother": {
"name": "Ma",
"parents": {
"mother": {
"name": "GraMa",
"parents": {}
},
"father": {
"name": "GraPa",
"parents": {}
}
}
},
"father": {
"name": "Pa",
"parents": {}
}
}
}
}
}
"""
final class Parents: Codable{
let mother: Person?
let father: Person?
}
final class Person: Codable{
let name: String
let parents: Parents?
}
final class RootPerson: Codable {
var rootPerson: Person
}
final class Root: Codable {
var familyTree: RootPerson
}
var jsonData = json.data(using: .utf8)!
do{
let familyTree = try JSONDecoder().decode(Root.self, from: jsonData)
print("Ma •••>", familyTree.familyTree.rootPerson.parents?.mother?.name)
print("GraPa •••>", familyTree.familyTree.rootPerson.parents?.mother?.parents?.father?.name)
print("Shoud be empty •••>", familyTree.familyTree.rootPerson.parents?.mother?.parents?.father?.parents?.father?.name)
} catch {
print(error)
}

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"}}}}}